Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> registry.identifyNonNullableName(nameNode.getQualifiedName()); } else if (info.hasTypedefType()) { registry.identifyNonNullableName(nameNode.getQualifiedName()); } } } } } private JSType getNativeType(JSTypeNative nativeType) { return typeRegistry.getNativeType(nativeType); } private abstract class AbstractScopeBuilder implements NodeTraversal.Callback { /** * The scope that we're builidng. */ final Scope scope; private final List<DeferredSetType> deferredSetTypes = Lists.newArrayList(); /** * Functions that we found in the global scope and not in externs. */ private final List<Node> nonExternFunctions = Lists.newArrayList(); /** * Type-less stubs. * * If at the end of traversal, we still don't have types for these * stubs, then we should declare UNKNOWN types. */ private final List<StubDeclaration> stubDeclarations = Lists.newArrayList(); /** * The current source file that we're in. */ private String sourceName = null; /** * The InputId of the current node. */ private InputId inputId; private AbstractScopeBuilder(Scope scope) { this.scope = scope; } void setDeferredType(Node node, JSType type) { deferredSetTypes.add(new DeferredSetType(node, type)); } void resolveTypes() { // Resolve types and attach them to nodes. for (DeferredSetType deferred : deferredSetTypes) { deferred.resolve(scope); } // Resolve types and attach them to scope slots. Iterator<Var> vars = scope.getVars(); while (vars.hasNext()) { vars.next().resolveType(typeParsingErrorReporter); } // Tell the type registry that any remaining types // are unknown. typeRegistry.resolveTypesInScope(scope); } @Override public final boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { inputId = t.getInputId(); if (n.isFunction() || n.isScript()) { Preconditions.checkNotNull(inputId); sourceName = NodeUtil.getSourceName(n); } // We do want to traverse the name of a named function, but we don't // want to traverse the arguments or body. boolean descend = parent == null || parent.getType() != Token.FUNCTION || n == parent.getFirstChild() || parent == scope.getRootNode(); if (descend) { // Handle hoisted functions on pre-order traversal, so that they // get hit before other things in the scope. if (NodeUtil.isStatementParent(n)) { for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { if (NodeUtil.

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>isHoistedFunctionDeclaration(child)) { defineFunctionLiteral(child, n); } } } } return descend; } @Override public void visit(NodeTraversal t, Node n, Node parent) { inputId = t.getInputId(); attachLiteralTypes(t, n); switch (n.getType()) { case Token.CALL: checkForClassDefiningCalls(t, n, parent); checkForCallingConventionDefiningCalls(n, delegateCallingConventions); break; case Token.FUNCTION: if (t.getInput() == null || !t.getInput().isExtern()) { nonExternFunctions.add(n); } // Hoisted functions are handled during pre-traversal. if (!NodeUtil.isHoistedFunctionDeclaration(n)) { defineFunctionLiteral(n, parent); } break; case Token.ASSIGN: // Handle initialization of properties. Node firstChild = n.getFirstChild(); if (firstChild.isGetProp() && firstChild.isQualifiedName()) { maybeDeclareQualifiedName(t, n.getJSDocInfo(), firstChild, n, firstChild.getNext()); } break; case Token.CATCH: defineCatch(n, parent); break; case Token.VAR: defineVar(n, parent); break; case Token.GETPROP: // Handle stubbed properties. if (parent.isExprResult() && n.isQualifiedName()) { maybeDeclareQualifiedName(t, n.getJSDocInfo(), n, parent, null); } break; } } private void attachLiteralTypes(NodeTraversal t, Node n) { switch (n.getType()) { case Token.NULL: n.setJSType(getNativeType(NULL_TYPE)); break; case Token.VOID: n.setJSType(getNativeType(VOID_TYPE)); break; case Token.STRING: // Defer keys to the Token.OBJECTLIT case if (!NodeUtil.isObjectLitKey(n, n.getParent())) { n.setJSType(getNativeType(STRING_TYPE)); } break; case Token.NUMBER: n.setJSType(getNativeType(NUMBER_TYPE)); break; case Token.TRUE: case Token.FALSE: n.setJSType(getNativeType(BOOLEAN_TYPE)); break; case Token.REGEXP: n.setJSType(getNativeType(REGEXP_TYPE)); break; case Token.OBJECTLIT: defineObjectLiteral(t, n); break; // NOTE(nicksantos): If we ever support Array tuples, // we will need to put ARRAYLIT here as well. } } private void defineObjectLiteral(NodeTraversal t, Node objectLit) { // Handle the @

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>Type.toMaybeEnumType().getElementsType() : NodeUtil.getObjectLitKeyTypeFromValueType(keyNode, valueType); if (keyType != null) { // Try to declare this property in the current scope if it // has an authoritative name. String qualifiedName = NodeUtil.getBestLValueName(keyNode); if (qualifiedName != null) { defineSlot(keyNode, objLit, qualifiedName, keyType, false); } else { setDeferredType(keyNode, keyType); } if (objLitType != null && declareOnOwner) { // Declare this property on its object literal. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); objLitType.defineDeclaredProperty(memberName, keyType, keyNode); } } } } /** * Returns the type specified in a JSDoc annotation near a GETPROP or NAME. * * Extracts type information from either the {@code @type} tag or from * the {@code @return} and {@code @param} tags. */ private JSType getDeclaredTypeInAnnotation(String sourceName, Node node, JSDocInfo info) { JSType jsType = null; Node objNode = node.isGetProp() ? node.getFirstChild() : NodeUtil.isObjectLitKey(node, node.getParent()) ? node.getParent() : null; if (info != null) { if (info.hasType()) { jsType = info.getType().evaluate(scope, typeRegistry); } else if (FunctionTypeBuilder.isFunctionTypeDeclaration(info)) { String fnName = node.getQualifiedName(); jsType = createFunctionTypeFromNodes( null, fnName, info, node); } } return jsType; } /** * Asserts that it's ok to define this node's name. * The node should have a source name and be of the specified type. */ void assertDefinitionNode(Node n, int type) { Preconditions.checkState(sourceName != null); Preconditions.checkState(n.getType() == type); } /** * Defines a catch parameter. */ void defineCatch(Node n, Node parent) { assertDefinitionNode(n, Token.CATCH); Node catchName = n.getFirstChild(); defineSlot(catchName, n, null); } /** * Defines a VAR initialization. */ void defineVar(Node n, Node parent) { assertDefinitionNode(n, Token.VAR); JSDocInfo info = n.getJSDocInfo(); if (n.hasMoreThanOneChild()) { if (info != null) { // multiple children compiler.report(JSError.make(sourceName, n, MULTIPLE_VAR_DEF)); } for (Node name :

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> n.children()) { defineName(name, n, parent, name.getJSDocInfo()); } } else { Node name = n.getFirstChild(); defineName(name, n, parent, (info != null) ? info : name.getJSDocInfo()); } } /** * Defines a function literal. */ void defineFunctionLiteral(Node n, Node parent) { assertDefinitionNode(n, Token.FUNCTION); // Determine the name and JSDocInfo and lvalue for the function. // Any of these may be null. Node lValue = NodeUtil.getBestLValue(n); JSDocInfo info = NodeUtil.getBestJSDocInfo(n); String functionName = NodeUtil.getBestLValueName(lValue); FunctionType functionType = createFunctionTypeFromNodes(n, functionName, info, lValue); // Assigning the function type to the function node setDeferredType(n, functionType); // Declare this symbol in the current scope iff it's a function // declaration. Otherwise, the declaration will happen in other // code paths. if (NodeUtil.isFunctionDeclaration(n)) { defineSlot(n.getFirstChild(), n, functionType); } } /** * Defines a variable based on the {@link Token#NAME} node passed. * @param name The {@link Token#NAME} node. * @param var The parent of the {@code name} node, which must be a * {@link Token#VAR} node. * @param parent {@code var}'s parent. * @param info the {@link JSDocInfo} information relating to this * {@code name} node. */ private void defineName(Node name, Node var, Node parent, JSDocInfo info) { Node value = name.getFirstChild(); // variable's type JSType type = getDeclaredType(sourceName, info, name, value); if (type == null) { // The variable's type will be inferred. type = name.isFromExterns() ? getNativeType(UNKNOWN_TYPE) : null; } defineSlot(name, var, type); } /** * If a variable is assigned a function literal in the global scope, * make that a declared type (even if there's no doc info). * There's only one exception to this rule: * if the return type is inferred, and we're in a local * scope, we should assume the whole function is inferred. */ private boolean shouldUseFunctionLiteralType( FunctionType type, JSDocInfo info, Node lValue) { if (info != null) { return true; } if (lValue != null && NodeUtil.isObjectLitKey(lValue, lValue.getParent())) { return false;

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> parent, type, type == null); } /** * Defines a typed variable. The defining node will be annotated with the * variable's type of {@link JSTypeNative#UNKNOWN_TYPE} if its type is * inferred. * * Slots may be any variable or any qualified name in the global scope. * * @param n the defining NAME or GETPROP node. * @param parent the {@code n}'s parent. * @param type the variable's type. It may be {@code null} if * {@code inferred} is {@code true}. */ void defineSlot(Node n, Node parent, JSType type, boolean inferred) { Preconditions.checkArgument(inferred || type != null); // Only allow declarations of NAMEs and qualfied names. // Object literal keys will have to compute their names themselves. if (n.isName()) { Preconditions.checkArgument( parent.isFunction() || parent.isVar() || parent.isParamList() || parent.isCatch()); } else { Preconditions.checkArgument( n.isGetProp() && (parent.isAssign() || parent.isExprResult())); } defineSlot(n, parent, n.getQualifiedName(), type, inferred); } /** * Defines a symbol in the current scope. * * @param n the defining NAME or GETPROP or object literal key node. * @param parent the {@code n}'s parent. * @param variableName The name that this should be known by. * @param type the variable's type. It may be {@code null} if * {@code inferred} is {@code true}. * @param inferred Whether the type is inferred or declared. */ void defineSlot(Node n, Node parent, String variableName, JSType type, boolean inferred) { Preconditions.checkArgument(!variableName.isEmpty()); boolean isGlobalVar = n.isName() && scope.isGlobal(); boolean shouldDeclareOnGlobalThis = isGlobalVar && (parent.isVar() || parent.isFunction()); // If n is a property, then we should really declare it in the // scope where the root object appears. This helps out people // who declare "global" names in an anonymous namespace. Scope scopeToDeclareIn = scope; if (n.isGetProp() && !scope.isGlobal() && isQnameRootedInGlobalScope(n)) { Scope globalScope = scope.getGlobalScope(); // don't try to declare in the global scope if there's // already a symbol there with this name. if (!globalScope.isDeclared(variableName, false)) { scopeToDeclareIn = scope.getGlobalScope(); } } // declared in closest scope? CompilerInput input = compiler.getInput(inputId); if (scopeToDeclareIn.isDeclared(

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>variableName, false)) { Var oldVar = scopeToDeclareIn.getVar(variableName); validator.expectUndeclaredVariable( sourceName, input, n, parent, oldVar, variableName, type); } else { if (!inferred) { setDeferredType(n, type); } // The input may be null if we are working with a AST snippet. boolean isExtern = n.isFromExterns(); Var newVar = scopeToDeclareIn.declare(variableName, n, type, input, inferred); if (type instanceof EnumType) { Node initialValue = newVar.getInitialValue(); boolean isValidValue = initialValue != null && (initialValue.isObjectLit() || initialValue.isQualifiedName()); if (!isValidValue) { compiler.report(JSError.make(sourceName, n, ENUM_INITIALIZER)); } } // We need to do some additional work for constructors and interfaces. FunctionType fnType = JSType.toMaybeFunctionType(type); if (fnType != null && // We don't want to look at empty function types. !type.isEmptyType()) { if ((fnType.isConstructor() || fnType.isInterface()) && !fnType.equals(getNativeType(U2U_CONSTRUCTOR_TYPE))) { // Declare var.prototype in the scope chain. FunctionType superClassCtor = fnType.getSuperClassConstructor(); ObjectType.Property prototypeSlot = fnType.getSlot("prototype"); String prototypeName = variableName + ".prototype"; // There are some rare cases where the prototype will already // be declared. See TypedScopeCreatorTest#testBogusPrototypeInit. // Fortunately, other warnings will complain if this happens. if (scopeToDeclareIn.getOwnSlot(prototypeName) == null) { // When we declare the function prototype implicitly, we // want to make sure that the function and its prototype // are declared at the same node. We also want to make sure // that the if a symbol has both a Var and a JSType, they have // the same node. // // This consistency is helpful to users of SymbolTable, // because everything gets declared at the same place. prototypeSlot.setNode(n); scopeToDeclareIn.declare(prototypeName, n, prototypeSlot.getType(), input, /* declared iff there's an explicit supertype */ superClassCtor == null || superClassCtor.getInstanceType().equals( getNativeType(OBJECT_TYPE))); } // Make sure the variable is initialized to something if // it constructs itself. if (newVar.getInitialValue() == null && !isExtern && // We want to make sure that when we declare a new instance // type (with @constructor) that there's actually a ctor for it. // This doesn't apply to structural

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> constructors // (like function(new:Array). Checking the constructed // type against the variable name is a sufficient check for // this. variableName.equals( fnType.getInstanceType().getReferenceName())) { compiler.report( JSError.make(sourceName, n, fnType.isConstructor() ? CTOR_INITIALIZER : IFACE_INITIALIZER, variableName)); } } } } if (shouldDeclareOnGlobalThis) { ObjectType globalThis = typeRegistry.getNativeObjectType(GLOBAL_THIS); if (inferred) { globalThis.defineInferredProperty(variableName, type == null ? getNativeType(JSTypeNative.NO_TYPE) : type, n); } else { globalThis.defineDeclaredProperty(variableName, type, n); } } if (isGlobalVar && "Window".equals(variableName) && type != null && type.isFunctionType() && type.isConstructor()) { FunctionType globalThisCtor = typeRegistry.getNativeObjectType(GLOBAL_THIS).getConstructor(); globalThisCtor.getInstanceType().clearCachedValues(); globalThisCtor.getPrototype().clearCachedValues(); globalThisCtor .setPrototypeBasedOn((type.toMaybeFunctionType()).getInstanceType()); } } /** * Check if the given node is a property of a name in the global scope. */ private boolean isQnameRootedInGlobalScope(Node n) { Scope scope = getQnameRootScope(n); return scope != null && scope.isGlobal(); } /** * Return the scope for the name of the given node. */ private Scope getQnameRootScope(Node n) { Node root = NodeUtil.getRootOfQualifiedName(n); if (root.isName()) { Var var = scope.getVar(root.getString()); if (var != null) { return var.getScope(); } } return null; } /** * Look for a type declaration on a property assignment * (in an ASSIGN or an object literal key). * * @param info The doc info for this property. * @param lValue The l-value node. * @param rValue The node that {@code n} is being initialized to, * or {@code null} if this is a stub declaration. */ private JSType getDeclaredType(String sourceName, JSDocInfo info, Node lValue, @Nullable Node rValue) { if (info != null && info.hasType()) { return getDeclaredTypeInAnnotation(sourceName, lValue, info); } else if (rValue != null && rValue.isFunction() && shouldUseFunctionLiteralType( JSType.toMaybeFunctionType(rValue.getJSType()), info, lValue)) { return rValue.

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>getJSType(); } else if (info != null) { if (info.hasEnumParameterType()) { if (rValue != null && rValue.isObjectLit()) { return rValue.getJSType(); } else { return createEnumTypeFromNodes( rValue, lValue.getQualifiedName(), info, lValue); } } else if (info.isConstructor() || info.isInterface()) { return createFunctionTypeFromNodes( rValue, lValue.getQualifiedName(), info, lValue); } else { // Check if this is constant, and if it has a known type. if (info.isConstant()) { JSType knownType = null; if (rValue != null) { if (rValue.getJSType() != null && !rValue.getJSType().isUnknownType()) { return rValue.getJSType(); } else if (rValue.isOr()) { // Check for a very specific JS idiom: // var x = x || TYPE; // This is used by Closure's base namespace for esoteric // reasons. Node firstClause = rValue.getFirstChild(); Node secondClause = firstClause.getNext(); boolean namesMatch = firstClause.isName() && lValue.isName() && firstClause.getString().equals(lValue.getString()); if (namesMatch && secondClause.getJSType() != null && !secondClause.getJSType().isUnknownType()) { return secondClause.getJSType(); } } } } } } return getDeclaredTypeInAnnotation(sourceName, lValue, info); } private FunctionType getFunctionType(@Nullable Var v) { JSType t = v == null ? null : v.getType(); ObjectType o = t == null ? null : t.dereference(); return JSType.toMaybeFunctionType(o); } /** * Look for calls that set a delegate method's calling convention. */ private void checkForCallingConventionDefiningCalls( Node n, Map<String, String> delegateCallingConventions) { codingConvention.checkForCallingConventionDefiningCalls(n, delegateCallingConventions); } /** * Look for class-defining calls. * Because JS has no 'native' syntax for defining classes, * this is often very coding-convention dependent and business-logic heavy. */ private void checkForClassDefiningCalls( NodeTraversal t, Node n, Node parent) { SubclassRelationship relationship = codingConvention.getClassesDefinedByCall(n); if (relationship != null) { FunctionType superCtor = getFunctionType( scope.getVar(relationship.superclassName)); FunctionType subCtor = getFunctionType( scope.getVar(relationship.subclassName)); if (superCtor != null && superCtor.isConstructor() && subCtor !=

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> null && subCtor.isConstructor()) { ObjectType superClass = superCtor.getInstanceType(); ObjectType subClass = subCtor.getInstanceType(); // superCtor and subCtor might be structural constructors // (like {function(new:Object)}) so we need to resolve them back // to the original ctor objects. superCtor = superClass.getConstructor(); subCtor = subClass.getConstructor(); if (relationship.type == SubclassType.INHERITS && !superClass.isEmptyType() && !subClass.isEmptyType()) { validator.expectSuperType(t, n, superClass, subClass); } if (superCtor != null && subCtor != null) { codingConvention.applySubclassRelationship( superCtor, subCtor, relationship.type); } } } String singletonGetterClassName = codingConvention.getSingletonGetterClassName(n); if (singletonGetterClassName != null) { ObjectType objectType = ObjectType.cast( typeRegistry.getType(singletonGetterClassName)); if (objectType != null) { FunctionType functionType = objectType.getConstructor(); if (functionType != null) { FunctionType getterType = typeRegistry.createFunctionType(objectType); codingConvention.applySingletonGetter(functionType, getterType, objectType); } } } DelegateRelationship delegateRelationship = codingConvention.getDelegateRelationship(n); if (delegateRelationship != null) { applyDelegateRelationship(delegateRelationship); } ObjectLiteralCast objectLiteralCast = codingConvention.getObjectLiteralCast(t, n); if (objectLiteralCast != null) { ObjectType type = ObjectType.cast( typeRegistry.getType(objectLiteralCast.typeName)); if (type != null && type.getConstructor() != null) { setDeferredType(objectLiteralCast.objectNode, type); } else { compiler.report(JSError.make(t.getSourceName(), n, CONSTRUCTOR_EXPECTED)); } } } /** * Apply special properties that only apply to delegates. */ private void applyDelegateRelationship( DelegateRelationship delegateRelationship) { ObjectType delegatorObject = ObjectType.cast( typeRegistry.getType(delegateRelationship.delegator)); ObjectType delegateBaseObject = ObjectType.cast( typeRegistry.getType(delegateRelationship.delegateBase)); ObjectType delegateSuperObject = ObjectType.cast( typeRegistry.getType(codingConvention.getDelegateSuperclassName())); if (delegatorObject != null && delegateBaseObject != null && delegateSuperObject != null) { FunctionType delegatorCtor = delegatorObject.getConstructor(); FunctionType delegateBaseCtor = delegateBaseObject.getConstructor(); FunctionType delegateSuperCtor = delegateSuperObject.getConstructor(); if (delegatorCtor != null && delegateBaseCtor != null && delegateSuperCtor != null) { FunctionParamBuilder functionParamBuilder = new FunctionParamBuilder(typeRegistry

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>); functionParamBuilder.addRequiredParams( getNativeType(U2U_CONSTRUCTOR_TYPE)); FunctionType findDelegate = typeRegistry.createFunctionType( typeRegistry.createDefaultObjectUnion(delegateBaseObject), functionParamBuilder.build()); FunctionType delegateProxy = typeRegistry.createConstructorType( delegateBaseObject.getReferenceName() + DELEGATE_PROXY_SUFFIX, null, null, null); delegateProxy.setPrototypeBasedOn(delegateBaseObject); codingConvention.applyDelegateRelationship( delegateSuperObject, delegateBaseObject, delegatorObject, delegateProxy, findDelegate); delegateProxyPrototypes.add(delegateProxy.getPrototype()); } } } /** * Declare the symbol for a qualified name in the global scope. * * @param info The doc info for this property. * @param n A top-level GETPROP node (it should not be contained inside * another GETPROP). * @param parent The parent of {@code n}. * @param rhsValue The node that {@code n} is being initialized to, * or {@code null} if this is a stub declaration. */ void maybeDeclareQualifiedName(NodeTraversal t, JSDocInfo info, Node n, Node parent, Node rhsValue) { Node ownerNode = n.getFirstChild(); String ownerName = ownerNode.getQualifiedName(); String qName = n.getQualifiedName(); String propName = n.getLastChild().getString(); Preconditions.checkArgument(qName != null && ownerName != null); // Precedence of type information on GETPROPs: // 1) @type annnotation / @enum annotation // 2) ASSIGN to FUNCTION literal // 3) @param/@return annotation (with no function literal) // 4) ASSIGN to something marked @const // 5) ASSIGN to anything else // // 1, 3, and 4 are declarations, 5 is inferred, and 2 is a declaration iff // the function has jsdoc or has not been declared before. // // FUNCTION literals are special because TypedScopeCreator is very smart // about getting as much type information as possible for them. // Determining type for #1 + #2 + #3 + #4 JSType valueType = getDeclaredType(t.getSourceName(), info, n, rhsValue); if (valueType == null && rhsValue != null) { // Determining type for #5 valueType = rhsValue.getJSType(); } // Function prototypes are special. // It's a common JS idiom to do: // F.prototype = { ... }; // So if F does not have an explicitly declared super type, // allow F.prototype to be redefined arbitrarily. if ("prototype".equals(propName)) { Var qVar = scope.getVar(qName); if

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> (qVar != null) { // If the programmer has declared that F inherits from Super, // and they assign F.prototype to an object literal, // then they are responsible for making sure that the object literal's // implicit prototype is set up appropriately. We just obey // the @extends tag. ObjectType qVarType = ObjectType.cast(qVar.getType()); if (qVarType != null && rhsValue != null && rhsValue.isObjectLit()) { typeRegistry.resetImplicitPrototype( rhsValue.getJSType(), qVarType.getImplicitPrototype()); } else if (!qVar.isTypeInferred()) { // If the programmer has declared that F inherits from Super, // and they assign F.prototype to some arbitrary expression, // there's not much we can do. We just ignore the expression, // and hope they've annotated their code in a way to tell us // what props are going to be on that prototype. return; } if (qVar.getScope() == scope) { scope.undeclare(qVar); } } } if (valueType == null) { if (parent.isExprResult()) { stubDeclarations.add(new StubDeclaration( n, t.getInput() != null && t.getInput().isExtern(), ownerName)); } return; } // NOTE(nicksantos): Determining whether a property is declared or not // is really really obnoxious. // // The problem is that there are two (equally valid) coding styles: // // (function() { // /* The authoritative definition of goog.bar. */ // goog.bar = function() {}; // })(); // // function f() { // goog.bar(); // /* Reset goog.bar to a no-op. */ // goog.bar = function() {}; // } ownerType.defineDeclaredProperty(propName, valueType, n); } } // If the property is already declared, the error will be // caught when we try to declare it in the current scope. defineSlot(n, parent, valueType, inferred); } else if (rhsValue != null && rhsValue.isTrue()) { // We declare these for delegate proxy method properties. FunctionType ownerType = JSType.toMaybeFunctionType(getObjectSlot(ownerName)); if (ownerType != null) { JSType ownerTypeOfThis = ownerType.getTypeOfThis(); String delegateName = codingConvention.getDelegateSuperclassName(); JSType delegateType = delegateName == null ? null : typeRegistry.getType(delegateName); if (delegateType != null && ownerTypeOfThis.isSubtype(delegateType)) { defineSlot(n, parent, getNativeType(BOOLEAN_TYPE), true); } } } } /**

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> * Find the ObjectType associated with the given slot. * @param slotName The name of the slot to find the type in. * @return An object type, or null if this slot does not contain an object. */ private ObjectType getObjectSlot(String slotName) { Var ownerVar = scope.getVar(slotName); if (ownerVar != null) { JSType ownerVarType = ownerVar.getType(); return ObjectType.cast(ownerVarType == null ? null : ownerVarType.restrictByNotNullOrUndefined()); } return null; } /** * Resolve any stub delcarations to unknown types if we could not * find types for them during traversal. */ void resolveStubDeclarations() { for (StubDeclaration stub : stubDeclarations) { Node n = stub.node; Node parent = n.getParent(); String qName = n.getQualifiedName(); String propName = n.getLastChild().getString(); String ownerName = stub.ownerName; boolean isExtern = stub.isExtern; if (scope.isDeclared(qName, false)) { continue; } // If we see a stub property, make sure to register this property // in the type registry. ObjectType ownerType = getObjectSlot(ownerName); ObjectType unknownType = typeRegistry.getNativeObjectType(UNKNOWN_TYPE); defineSlot(n, parent, unknownType, true); if (ownerType != null && (isExtern || ownerType.isFunctionPrototypeType())) { // If this is a stub for a prototype, just declare it // as an unknown type. These are seen often in externs. ownerType.defineInferredProperty( propName, unknownType, n); } else { typeRegistry.registerPropertyOnType( propName, ownerType == null ? unknownType : ownerType); } } } /** * Collects all declared properties in a function, and * resolves them relative to the global scope. */ private final class CollectProperties extends AbstractShallowStatementCallback { private final ObjectType thisType; CollectProperties(ObjectType thisType) { this.thisType = thisType; } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isExprResult()) { Node child = n.getFirstChild(); switch (child.getType()) { case Token.ASSIGN: maybeCollectMember(t, child.getFirstChild(), child, child.getLastChild()); break; case Token.GETPROP: maybeCollectMember(t, child, child, null); break; } } } private void maybeCollectMember(NodeTraversal t, Node member, Node nodeWithJsDocInfo, @Nullable Node value) { JSDocInfo info = nodeWithJsDocInfo.getJSDocInfo(); // Do nothing if there is no J

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>SDoc type info, or // if the node is not a member expression, or // if the member expression is not of the form: this.someProperty. if (info == null || member.getType() != Token.GETPROP || member.getFirstChild().getType() != Token.THIS) { return; } member.getFirstChild().setJSType(thisType); JSType jsType = getDeclaredType(t.getSourceName(), info, member, value); Node name = member.getLastChild(); if (jsType != null && (name.isName() || name.isString())) { thisType.defineDeclaredProperty( name.getString(), jsType, member); } } } // end CollectProperties } /** * A stub declaration without any type information. */ private static final class StubDeclaration { private final Node node; private final boolean isExtern; private final String ownerName; private StubDeclaration(Node node, boolean isExtern, String ownerName) { this.node = node; this.isExtern = isExtern; this.ownerName = ownerName; } } /** * A shallow traversal of the global scope to build up all classes, * functions, and methods. */ private final class GlobalScopeBuilder extends AbstractScopeBuilder { private GlobalScopeBuilder(Scope scope) { super(scope); } /** * Visit a node in the global scope, and add anything it declares to the * global symbol table. * * @param t The current traversal. * @param n The node being visited. * @param parent The parent of n */ @Override public void visit(NodeTraversal t, Node n, Node parent) { super.visit(t, n, parent); switch (n.getType()) { case Token.VAR: // Handle typedefs. if (n.hasOneChild()) { checkForTypedef(t, n.getFirstChild(), n.getJSDocInfo()); } break; } } @Override void maybeDeclareQualifiedName( NodeTraversal t, JSDocInfo info, Node n, Node parent, Node rhsValue) { checkForTypedef(t, n, info); super.maybeDeclareQualifiedName(t, info, n, parent, rhsValue); } /** * Handle typedefs. * @param t The current traversal. * @param candidate A qualified name node. * @param info JSDoc comments. */ private void checkForTypedef( NodeTraversal t, Node candidate, JSDocInfo info) { if (info == null || !info.hasTypedefType()) { return; } String typedef = candidate.getQualifiedName(); if (typedef == null) { return; } // TODO(nicksantos|user): This is a ter

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>rible, terrible hack // to bail out on recusive typedefs. We'll eventually need // to handle these properly. typeRegistry.declareType(typedef, getNativeType(UNKNOWN_TYPE)); JSType realType = info.getTypedefType().evaluate(scope, typeRegistry); if (realType == null) { compiler.report( JSError.make( t.getSourceName(), candidate, MALFORMED_TYPEDEF, typedef)); } typeRegistry.overwriteDeclaredType(typedef, realType); if (candidate.isGetProp()) { defineSlot(candidate, candidate.getParent(), getNativeType(NO_TYPE), false); } } } // end GlobalScopeBuilder /** * A shallow traversal of a local scope to find all arguments and * local variables. */ private final class LocalScopeBuilder extends AbstractScopeBuilder { /** * @param scope The scope that we're builidng. */ private LocalScopeBuilder(Scope scope) { super(scope); } /** * Traverse the scope root and build it. */ void build() { NodeTraversal.traverse(compiler, scope.getRootNode(), this); AstFunctionContents contents = getFunctionAnalysisResults(scope.getRootNode()); if (contents != null) { for (String varName : contents.getEscapedVarNames()) { Var v = scope.getVar(varName); Preconditions.checkState(v.getScope() == scope); v.markEscaped(); } } } /** * Visit a node in a local scope, and add any local variables or catch * parameters into the local symbol table. * * @param t The node traversal. * @param n The node being visited. * @param parent The parent of n */ @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n == scope.getRootNode()) return; if (n.isParamList() && parent == scope.getRootNode()) { handleFunctionInputs(parent); return; } super.visit(t, n, parent); } /** Handle bleeding functions and function parameters. */ private void handleFunctionInputs(Node fnNode) { // Handle bleeding functions. Node fnNameNode = fnNode.getFirstChild(); String fnName = fnNameNode.getString(); if (!fnName.isEmpty()) { Scope.Var fnVar = scope.getVar(fnName); if (fnVar == null || // Make sure we're not touching a native function. Native // functions aren't bleeding, but may not have a declaration // node. (fnVar.getNameNode() != null && // Make sure that the function is actually bleeding by checking // if has already been declared. fnVar.getInitialValue() != fnNode)) { define

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>Slot(fnNameNode, fnNode, fnNode.getJSType(), false); } } declareArguments(fnNode); } /** * Declares all of a function's arguments. */ private void declareArguments(Node functionNode) { Node astParameters = functionNode.getFirstChild().getNext(); Node body = astParameters.getNext(); FunctionType functionType = JSType.toMaybeFunctionType(functionNode.getJSType()); if (functionType != null) { Node jsDocParameters = functionType.getParametersNode(); if (jsDocParameters != null) { Node jsDocParameter = jsDocParameters.getFirstChild(); for (Node astParameter : astParameters.children()) { if (jsDocParameter != null) { defineSlot(astParameter, functionNode, jsDocParameter.getJSType(), false); jsDocParameter = jsDocParameter.getNext(); } else { defineSlot(astParameter, functionNode, null, true); } } } } } // end declareArguments } // end LocalScopeBuilder /** * Does a first-order function analysis that just looks at simple things * like what variables are escaped, and whether 'this' is used. */ private static class FirstOrderFunctionAnalyzer extends AbstractScopedCallback implements CompilerPass { private final AbstractCompiler compiler; private final Map<Node, AstFunctionContents> data; FirstOrderFunctionAnalyzer( AbstractCompiler compiler, Map<Node, AstFunctionContents> outParam) { this.compiler = compiler; this.data = outParam; } @Override public void process(Node externs, Node root) { if (externs == null) { NodeTraversal.traverse(compiler, root, this); } else { NodeTraversal.traverseRoots( compiler, ImmutableList.of(externs, root), this); } } @Override public void enterScope(NodeTraversal t) { if (!t.inGlobalScope()) { Node n = t.getScopeRoot(); data.put(n, new AstFunctionContents(n)); } } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (t.inGlobalScope()) { return; } if (n.isReturn() && n.getFirstChild() != null) { data.get(t.getScopeRoot()).recordNonEmptyReturn(); } else if (n.isName() && NodeUtil.isLValue(n)) { String name = n.getString(); Scope scope = t.getScope(); Var var = scope.getVar(name); if (var != null) { Scope ownerScope = var.getScope(); if (scope != ownerScope && ownerScope.isLocal()) { data.get(ownerScope.getRootNode()).recordEscapedVarName(name); } } } }

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> } private AstFunctionContents getFunctionAnalysisResults(@Nullable Node n) { if (n == null) { return null; } // Sometimes this will return null in things like // NameReferenceGraphConstruction that build partial scopes. return functionAnalysisResults.get(n); } }

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>/* * Copyright 2004 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Preconditions; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.*; /** * Verifies that constants are only assigned a value once. * e.g. var XX = 5; * XX = 3; // error! * XX++; // error! * */ class ConstCheck extends AbstractPostOrderCallback implements CompilerPass { static final DiagnosticType CONST_REASSIGNED_VALUE_ERROR = DiagnosticType.error( "JSC_CONSTANT_REASSIGNED_VALUE_ERROR", "constant {0} assigned a value more than once"); private final AbstractCompiler compiler; private final Set<Scope.Var> initializedConstants; /** * Creates an instance. */ public ConstCheck(AbstractCompiler compiler) { this.compiler = compiler; this.initializedConstants = new HashSet<Scope.Var>(); } @Override public void process(Node externs, Node root) { Preconditions.checkState(compiler.getLifeCycleStage().isNormalized()); NodeTraversal.traverse(compiler, root, this); } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.NAME: if (parent != null && parent.isVar() && n.hasChildren()) { String name = n.getString(); Scope.Var var = t.getScope().getVar(name); if (isConstant(var)) { if (initializedConstants.contains(var)) { reportError(t, n, name); } else { initializedConstants.add(var); } } } break; case Token.ASSIGN: case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITAND: case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.ASSIGN_URSH: case Token.ASSIGN_ADD: case Token.ASSIGN

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>_SUB: case Token.ASSIGN_MUL: case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: { Node lhs = n.getFirstChild(); if (lhs.isName()) { String name = lhs.getString(); Scope.Var var = t.getScope().getVar(name); if (isConstant(var)) { if (initializedConstants.contains(var)) { reportError(t, n, name); } else { initializedConstants.add(var); } } } break; } case Token.INC: case Token.DEC: { Node lhs = n.getFirstChild(); if (lhs.isName()) { String name = lhs.getString(); Scope.Var var = t.getScope().getVar(name); if (isConstant(var)) { reportError(t, n, name); } } break; } } } /** * Gets whether a variable is a constant initialized to a literal value at * the point where it is declared. */ private boolean isConstant(Scope.Var var) { return var != null && var.isConst(); } /** * Reports a reassigned constant error. */ void reportError(NodeTraversal t, Node n, String name) { compiler.report(t.makeError(n, CONST_REASSIGNED_VALUE_ERROR, name)); } }

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>name, true); } @Override public boolean isPrivate(String name) { return nextConvention.isPrivate(name); } @Override public SubclassRelationship getClassesDefinedByCall(Node callNode) { return nextConvention.getClassesDefinedByCall(callNode); } @Override public boolean isSuperClassReference(String propertyName) { return nextConvention.isSuperClassReference(propertyName); } @Override public String extractClassNameIfProvide(Node node, Node parent) { return nextConvention.extractClassNameIfProvide(node, parent); } @Override public String extractClassNameIfRequire(Node node, Node parent) { return nextConvention.extractClassNameIfRequire(node, parent); } @Override public String getExportPropertyFunction() { return nextConvention.getExportPropertyFunction(); } @Override public String getExportSymbolFunction() { return nextConvention.getExportSymbolFunction(); } @Override public List<String> identifyTypeDeclarationCall(Node n) { return nextConvention.identifyTypeDeclarationCall(n); } @Override public void applySubclassRelationship(FunctionType parentCtor, FunctionType childCtor, SubclassType type) { nextConvention.applySubclassRelationship( parentCtor, childCtor, type); } @Override public String getAbstractMethodName() { return nextConvention.getAbstractMethodName(); } @Override public String getSingletonGetterClassName(Node callNode) { return nextConvention.getSingletonGetterClassName(callNode); } @Override public void applySingletonGetter(FunctionType functionType, FunctionType getterType, ObjectType objectType) { nextConvention.applySingletonGetter( functionType, getterType, objectType); } @Override public DelegateRelationship getDelegateRelationship(Node callNode) { return nextConvention.getDelegateRelationship(callNode); } @Override public void applyDelegateRelationship( ObjectType delegateSuperclass, ObjectType delegateBase, ObjectType delegator, FunctionType delegateProxy, FunctionType findDelegate) { nextConvention.applyDelegateRelationship( delegateSuperclass, delegateBase, delegator, delegateProxy, findDelegate); } @Override public String getDelegateSuperclassName() { return nextConvention.getDelegateSuperclassName(); } @Override public void checkForCallingConventionDefiningCalls( Node n, Map<String, String> delegateCallingConventions) { nextConvention.checkForCallingConventionDefiningCalls( n, delegateCallingConventions); } @Override public void defineDelegateProxyPrototypeProperties( JSTypeRegistry registry, Scope scope, List<ObjectType> delegateProxyPrototypes, Map<String, String> delegateCallingConventions) { nextConvention.defineDelegateProxyPrototypeProperties( registry, scope, delegateProxyPrototypes, delegateCallingConventions); } @Override public String getGlobalObject() { return next

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>Convention.getGlobalObject(); } @Override public Collection<AssertionFunctionSpec> getAssertionFunctions() { return nextConvention.getAssertionFunctions(); } @Override public Bind describeFunctionBind(Node n) { return nextConvention.describeFunctionBind(n); } @Override public boolean isPropertyTestFunction(Node call) { return nextConvention.isPropertyTestFunction(call); } @Override public ObjectLiteralCast getObjectLiteralCast(NodeTraversal t, Node callNode) { return nextConvention.getObjectLiteralCast(t, callNode); } } /** * The default coding convention. * Should be at the bottom of all proxy chains. */ private static class DefaultCodingConvention implements CodingConvention { private static final long serialVersionUID = 1L; @Override public boolean isConstant(String variableName) { return false; } @Override public boolean isConstantKey(String variableName) { return false; } @Override public boolean isValidEnumKey(String key) { return key != null && key.length() > 0; } @Override public boolean isOptionalParameter(Node parameter) { // be as lax as possible, but this must be mutually exclusive from // var_args parameters. return !isVarArgsParameter(parameter); } @Override public boolean isVarArgsParameter(Node parameter) { // be as lax as possible return parameter.getParent().getLastChild() == parameter; } @Override public boolean isExported(String name, boolean local) { return local && name.startsWith("$super"); } @Override public boolean isExported(String name) { return isExported(name, false) || isExported(name, true); } @Override public boolean isPrivate(String name) { return false; } @Override public SubclassRelationship getClassesDefinedByCall(Node callNode) { return null; } @Override public boolean isSuperClassReference(String propertyName) { return false; } @Override public String extractClassNameIfProvide(Node node, Node parent) { String message = "only implemented in GoogleCodingConvention"; throw new UnsupportedOperationException(message); } @Override public String extractClassNameIfRequire(Node node, Node parent) { String message = "only implemented in GoogleCodingConvention"; throw new UnsupportedOperationException(message); } @Override public String getExportPropertyFunction() { return null; } @Override public String getExportSymbolFunction() { return null; } @Override public List<String> identifyTypeDeclarationCall(Node n) { return null; } @Override public void applySubclassRelationship(FunctionType parentCtor, FunctionType childCtor, SubclassType type) { // do nothing } @Override public String getAbstractMethodName() { return null

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>; } @Override public String getSingletonGetterClassName(Node callNode) { return null; } @Override public void applySingletonGetter(FunctionType functionType, FunctionType getterType, ObjectType objectType) { // do nothing. } @Override public DelegateRelationship getDelegateRelationship(Node callNode) { return null; } @Override public void applyDelegateRelationship( ObjectType delegateSuperclass, ObjectType delegateBase, ObjectType delegator, FunctionType delegateProxy, FunctionType findDelegate) { // do nothing. } @Override public String getDelegateSuperclassName() { return null; } @Override public void checkForCallingConventionDefiningCalls(Node n, Map<String, String> delegateCallingConventions) { // do nothing. } @Override public void defineDelegateProxyPrototypeProperties( JSTypeRegistry registry, Scope scope, List<ObjectType> delegateProxyPrototypes, Map<String, String> delegateCallingConventions) { // do nothing. } @Override public String getGlobalObject() { return "window"; } @Override public boolean isPropertyTestFunction(Node call) { return false; } @Override public ObjectLiteralCast getObjectLiteralCast(NodeTraversal t, Node callNode) { return null; } @Override public Collection<AssertionFunctionSpec> getAssertionFunctions() { return Collections.emptySet(); } @Override public Bind describeFunctionBind(Node n) { // It would be nice to be able to identify a fn.bind call // but that requires knowing the type of "fn". if (n.getType() != Token.CALL) { return null; } Node callTarget = n.getFirstChild(); String name = callTarget.getQualifiedName(); if (name != null) { if (name.equals("Function.prototype.bind.call")) { // goog.bind(fn, self, args...); Node fn = callTarget.getNext(); if (fn == null) { return null; } Node thisValue = safeNext(fn); Node parameters = safeNext(thisValue); return new Bind(fn, thisValue, parameters); } } if (callTarget.isGetProp() && callTarget.getLastChild().getString().equals("bind") && callTarget.getFirstChild().isFunction()) { // (function(){}).bind(self, args...); Node fn = callTarget.getFirstChild(); Node thisValue = callTarget.getNext(); Node parameters = safeNext(thisValue); return new Bind(fn, thisValue, parameters); } return null; } private Node safeNext(Node n) { if (n != null) { return n.getNext(); } return null; } } }

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>() { // By default, do nothing. Not all kinds of SourceFiles can regenerate // code. } boolean hasSourceInMemory() { return code != null; } /** Returns a unique name for the source file. */ @Override public String getName() { return fileName; } /** Returns whether this is an extern. */ @Override public boolean isExtern() { return isExternFile; } /** Sets that this is an extern. */ void setIsExtern(boolean newVal) { isExternFile = newVal; } /** * Gets the source line for the indicated line number. * * @param lineNumber the line number, 1 being the first line of the file. * @return The line indicated. Does not include the newline at the end * of the file. Returns {@code null} if it does not exist, * or if there was an IO exception. */ public String getLine(int lineNumber) { String js = ""; try { // NOTE(nicksantos): Right now, this is optimized for few warnings. // This is probably the right trade-off, but will be slow if there // are lots of warnings in one file. js = getCode(); } catch (IOException e) { return null; } int pos = 0; int startLine = 1; // If we've saved a previous offset and it's for a line less than the // one we're searching for, then start at that point. if (lineNumber >= lastLine) { pos = lastOffset; startLine = lastLine; } for (int n = startLine; n < lineNumber; n++) { int nextpos = js.indexOf('\n', pos); if (nextpos == -1) { return null; } pos = nextpos + 1; } // Remember this offset for the next search we do. lastOffset = pos; lastLine = lineNumber; if (js.indexOf('\n', pos) == -1) { // If next new line cannot be found, there are two cases // 1. pos already reaches the end of file, then null should be returned // 2. otherwise, return the contents between pos and the end of file. if (pos >= js.length()) { return null; } else { return js.substring(pos, js.length()); } } else { return js.substring(pos, js.indexOf('\n', pos)); } } /** * Get a region around the indicated line number. The exact definition of a * region is implementation specific, but it must contain the line indicated * by the line number. A region must not start or end by a carriage return. * * @param lineNumber the line number, 1 being the first line of the file.

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> * @return The line indicated. Returns {@code null} if it does not exist, * or if there was an IO exception. */ public Region getRegion(int lineNumber) { String js = ""; try { js = getCode(); } catch (IOException e) { return null; } int pos = 0; int startLine = Math.max(1, lineNumber - (SOURCE_EXCERPT_REGION_LENGTH + 1) / 2 + 1); for (int n = 1; n < startLine; n++) { int nextpos = js.indexOf('\n', pos); if (nextpos == -1) { break; } pos = nextpos + 1; } int end = pos; int endLine = startLine; for (int n = 0; n < SOURCE_EXCERPT_REGION_LENGTH; n++, endLine++) { end = js.indexOf('\n', end); if (end == -1) { break; } end++; } if (lineNumber >= endLine) { return null; } if (end == -1) { int last = js.length() - 1; if (js.charAt(last) == '\n') { return new SimpleRegion(startLine, endLine, js.substring(pos, last)); } else { return new SimpleRegion(startLine, endLine, js.substring(pos)); } } else { return new SimpleRegion(startLine, endLine, js.substring(pos, end)); } } @Override public String toString() { return fileName; } public static SourceFile fromFile(String fileName, Charset c) { return builder().withCharset(c).buildFromFile(fileName); } public static SourceFile fromFile(String fileName) { return builder().buildFromFile(fileName); } public static SourceFile fromFile(File file, Charset c) { return builder().withCharset(c).buildFromFile(file); } public static SourceFile fromFile(File file) { return builder().buildFromFile(file); } public static SourceFile fromCode(String fileName, String code) { return builder().buildFromCode(fileName, code); } public static SourceFile fromCode(String fileName, String originalPath, String code) { return builder().withOriginalPath(originalPath) .buildFromCode(fileName, code); } public static SourceFile fromInputStream(String fileName, InputStream s) throws IOException { return builder().buildFromInputStream(fileName, s); } public static SourceFile fromInputStream(String fileName, String originalPath, InputStream s) throws IOException { return builder().withOriginalPath(originalPath) .buildFromInputStream(fileName, s); } public static SourceFile fromReader(

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>/* * Copyright 2006 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.javascript.jscomp.CheckLevel; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; /** * Checks for non side effecting statements such as * <pre> * var s = "this string is " * "continued on the next line but you forgot the +"; * x == foo(); // should that be '='? * foo();; // probably just a stray-semicolon. Doesn't hurt to check though * </p> * and generates warnings. * */ final class CheckSideEffects extends AbstractPostOrderCallback { static final DiagnosticType USELESS_CODE_ERROR = DiagnosticType.warning( "JSC_USELESS_CODE", "Suspicious code. {0}"); private final CheckLevel level; CheckSideEffects(CheckLevel level) { this.level = level; } @Override public void visit(NodeTraversal t, Node n, Node parent) { // VOID nodes appear when there are extra semicolons at the BLOCK level. // I've been unable to think of any cases where this indicates a bug, // and apparently some people like keeping these semicolons around, // so we'll allow it. if (n.isEmpty() || n.isComma()) { return; } if (parent == null) return; int pt = parent.getType(); if (pt == Token.COMMA) { Node gramps = parent.getParent(); if (gramps.isCall() && parent == gramps.getFirstChild()) { // Semantically, a direct call to eval is different from an indirect // call to an eval. See Ecma-262 S15.1.2.1. So it's ok for the first // expression to a comma to be a no-op if it's used to indirect // an eval. if (n == parent.getFirstChild() && parent.getChildCount() == 2 && n.getNext().isName()

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> && "eval".equals(n.getNext().getString())) { return; } } if (n == parent.getLastChild()) { for (Node an : parent.getAncestors()) { int ancestorType = an.getType(); if (ancestorType == Token.COMMA) continue; if (ancestorType != Token.EXPR_RESULT && ancestorType != Token.BLOCK) return; else break; } } } else if (pt != Token.EXPR_RESULT && pt != Token.BLOCK) { if (pt == Token.FOR && parent.getChildCount() == 4 && (n == parent.getFirstChild() || n == parent.getFirstChild().getNext().getNext())) { // Fall through and look for warnings for the 1st and 3rd child // of a for. } else { return; // it might be ok to not have a side-effect } } boolean isSimpleOp = NodeUtil.isSimpleOperatorType(n.getType()); if (isSimpleOp || !NodeUtil.mayHaveSideEffects(n, t.getCompiler())) { if (n.isQualifiedName() && n.getJSDocInfo() != null) { // This no-op statement was there so that JSDoc information could // be attached to the name. This check should not complain about it. return; } else if (NodeUtil.isExpressionNode(n)) { // we already reported the problem when we visited the child. return; } String msg = "This code lacks side-effects. Is there a bug?"; if (n.isString()) { msg = "Is there a missing '+' on the previous line?"; } else if (isSimpleOp) { msg = "The result of the '" + Node.tokenToName(n.getType()) + "' operator is not being used."; } t.getCompiler().report( t.makeError(n, level, USELESS_CODE_ERROR, msg)); } } }

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>/* * Copyright 2009 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.jstype.StaticSymbolTable; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; /** * Memoize a scope creator. * * This allows you to make multiple passes, without worrying about * the expense of generating Scope objects over and over again. * * On the other hand, you also have to be more aware of what your passes * are doing. Scopes are memoized stupidly, so if the underlying tree * changes, the scope may be out of sync. * * @author nicksantos@google.com (Nick Santos) */ class MemoizedScopeCreator implements ScopeCreator, StaticSymbolTable<Var, Var> { private final Map<Node, Scope> scopes = Maps.newHashMap(); private final ScopeCreator delegate; /** * @param delegate The real source of Scope objects. */ MemoizedScopeCreator(ScopeCreator delegate) { this.delegate = delegate; } @Override public Iterable<Var> getReferences(Var var) { return ImmutableList.of(var); } @Override public Scope getScope(Var var) { return var.scope; } @Override public Iterable<Var> getAllSymbols() { List<Var> vars = Lists.newArrayList(); for (Scope s : scopes.values()) { Iterables.addAll(vars, s.getAllSymbols()); } return vars; } @Override public Scope createScope(Node n, Scope parent) { Scope scope = scopes.get(n); if (scope == null) { scope

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> = delegate.createScope(n, parent); scopes.put(n, scope); } else { Preconditions.checkState(parent == scope.getParent()); } return scope; } Collection<Scope> getAllMemoizedScopes() { return Collections.unmodifiableCollection(scopes.values()); } Scope getScopeIfMemoized(Node n) { return scopes.get(n); } /** * Removes all scopes with root nodes from a given script file. * * @param scriptName the name of the script file to remove nodes for. */ void removeScopesForScript(String scriptName) { for (Node scopeRoot : ImmutableSet.copyOf(scopes.keySet())) { if (scriptName.equals(scopeRoot.getSourceFileName())) { scopes.remove(scopeRoot); } } } }

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>/* * Copyright 2008 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Preconditions; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.JSTypeExpression; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; /** * Prepare the AST before we do any checks or optimizations on it. * * This pass must run. It should bring the AST into a consistent state, * and add annotations where necessary. It should not make any transformations * on the tree that would lose source information, since we need that source * information for checks. * * @author johnlenz@google.com (John Lenz) */ class PrepareAst implements CompilerPass { private final AbstractCompiler compiler; private final boolean checkOnly; PrepareAst(AbstractCompiler compiler) { this(compiler, false); } PrepareAst(AbstractCompiler compiler, boolean checkOnly) { this.compiler = compiler; this.checkOnly = checkOnly; } private void reportChange() { if (checkOnly) { Preconditions.checkState(false, "normalizeNodeType constraints violated"); } } @Override public void process(Node externs, Node root) { if (checkOnly) { normalizeNodeTypes(root); } else { // Don't perform "PrepareAnnotations" when doing checks as // they currently aren't valid during sanity checks. In particular, // they DIRECT_EVAL shouldn't be applied after inlining has been // performed. if (externs != null) { NodeTraversal.traverse( compiler, externs, new PrepareAnnotations(compiler)); } if (root != null) { NodeTraversal.traverse( compiler, root, new PrepareAnnotations(compiler)); } } } /** * Covert EXPR_VOID to EXPR_RESULT to simplify the rest of the code. */ private void normalizeNodeTypes(Node n) { normalizeBlocks(n); for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { // This pass is run during the CompilerTestCase validation

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>, so this // parent pointer check serves as a more general check. Preconditions.checkState(child.getParent() == n); normalizeNodeTypes(child); } } /** * Add blocks to IF, WHILE, DO, etc. */ private void normalizeBlocks(Node n) { if (NodeUtil.isControlStructure(n) && n.getType() != Token.LABEL && n.getType() != Token.SWITCH) { for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (NodeUtil.isControlStructureCodeBlock(n,c) && c.getType() != Token.BLOCK) { Node newBlock = new Node(Token.BLOCK, n.getLineno(), n.getCharno()); newBlock.copyInformationFrom(n); n.replaceChild(c, newBlock); if (c.getType() != Token.EMPTY) { newBlock.addChildrenToFront(c); } else { newBlock.setWasEmptyNode(true); } c = newBlock; reportChange(); } } } } /** * Normalize where annotations appear on the AST. Copies * around existing JSDoc annotations as well as internal annotations. */ static class PrepareAnnotations implements NodeTraversal.Callback { private final CodingConvention convention; PrepareAnnotations(AbstractCompiler compiler) { this.convention = compiler.getCodingConvention(); } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.isObjectLit()) { normalizeObjectLiteralAnnotations(n); } return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.CALL: annotateCalls(n); break; case Token.FUNCTION: annotateFunctions(n, parent); annotateDispatchers(n, parent); break; } } private void normalizeObjectLiteralAnnotations(Node objlit) { Preconditions.checkState(objlit.isObjectLit()); for (Node key = objlit.getFirstChild(); key != null; key = key.getNext()) { Node value = key.getFirstChild(); normalizeObjectLiteralKeyAnnotations(objlit, key, value); } } /** * There are two types of calls we are interested in calls without explicit * "this" values (what we are call "free" calls) and direct call to eval. */ private void annotateCalls(Node n) { Preconditions.checkState(n.isCall()); // Keep track of of the "this" context of a call. A call without an // explicit "this" is a free call. Node first = n.getFirstChild(); if (!NodeUtil.isGet(first)) { n.putBooleanProp(

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>Node.FREE_CALL, true); } // Keep track of the context in which eval is called. It is important // to distinguish between "(0, eval)()" and "eval()". if (first.isName() && "eval".equals(first.getString())) { first.putBooleanProp(Node.DIRECT_EVAL, true); } } /** * Translate dispatcher info into the property expected node. */ private void annotateDispatchers(Node n, Node parent) { Preconditions.checkState(n.isFunction()); if (parent.getJSDocInfo() != null && parent.getJSDocInfo().isJavaDispatch()) { if (parent.isAssign()) { Preconditions.checkState(parent.getLastChild() == n); n.putBooleanProp(Node.IS_DISPATCHER, true); } } } /** * In the AST that Rhino gives us, it needs to make a distinction * between jsdoc on the object literal node and jsdoc on the object literal * value. For example, * <pre> * var x = { * / JSDOC / * a: 'b', * c: / JSDOC / 'd' * }; * </pre> * * But in few narrow cases (in particular, function literals), it's * a lot easier for us if the doc is attached to the value. */ private void normalizeObjectLiteralKeyAnnotations( Node objlit, Node key, Node value) { Preconditions.checkState(objlit.isObjectLit()); if (key.getJSDocInfo() != null && value.isFunction()) { value.setJSDocInfo(key.getJSDocInfo()); } } /** * Annotate optional and var_arg function parameters. */ private void annotateFunctions(Node n, Node parent) { JSDocInfo fnInfo = NodeUtil.getFunctionJSDocInfo(n); // Compute which function parameters are optional and // which are var_args. Node args = n.getFirstChild().getNext(); for (Node arg = args.getFirstChild(); arg != null; arg = arg.getNext()) { String argName = arg.getString(); JSTypeExpression typeExpr = fnInfo == null ? null : fnInfo.getParameterType(argName); if (convention.isOptionalParameter(arg) || typeExpr != null && typeExpr.isOptionalArg()) { arg.putBooleanProp(Node.IS_OPTIONAL_PARAM, true); } if (convention.isVarArgsParameter(arg) || typeExpr != null && typeExpr.isVarArgs()) { arg.putBooleanProp(Node.IS_VAR_ARGS_PARAM, true); } } } } }

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>_VAR_DECLARATION, HIDDEN_PROPERTY_MISMATCH, INTERFACE_METHOD_NOT_IMPLEMENTED, HIDDEN_INTERFACE_PROPERTY_MISMATCH); TypeValidator(AbstractCompiler compiler) { this.compiler = compiler; this.typeRegistry = compiler.getTypeRegistry(); this.allValueTypes = typeRegistry.createUnionType( STRING_TYPE, NUMBER_TYPE, BOOLEAN_TYPE, NULL_TYPE, VOID_TYPE); this.nullOrUndefined = typeRegistry.createUnionType( NULL_TYPE, VOID_TYPE); } /** * Gets a list of type violations. * * For each violation, one element is the expected type and the other is * the type that is actually found. Order is not signficant. */ Iterable<TypeMismatch> getMismatches() { return mismatches; } void setShouldReport(boolean report) { this.shouldReport = report; } // All non-private methods should have the form: // expectCondition(NodeTraversal t, Node n, ...); // If there is a mismatch, the {@code expect} method should issue // a warning and attempt to correct the mismatch, when possible. /** * Expect the type to be an object, or a type convertible to object. If the * expectation is not met, issue a warning at the provided node's source code * position. * @return True if there was no warning, false if there was a mismatch. */ boolean expectObject(NodeTraversal t, Node n, JSType type, String msg) { if (!type.matchesObjectContext()) { mismatch(t, n, msg, type, OBJECT_TYPE); return false; } return true; } /** * Expect the type to be an object. Unlike expectObject, a type convertible * to object is not acceptable. */ void expectActualObject(NodeTraversal t, Node n, JSType type, String msg) { if (!type.isObject()) { mismatch(t, n, msg, type, OBJECT_TYPE); } } /** * Expect the type to contain an object sometimes. If the expectation is * not met, issue a warning at the provided node's source code position. */ void expectAnyObject(NodeTraversal t, Node n, JSType type, String msg) { JSType anyObjectType = getNativeType(NO_OBJECT_TYPE); if (!anyObjectType.isSubtype(type) && !type.isEmptyType()) { mismatch(t, n, msg, type, anyObjectType); } } /** * Expect the type to be a string, or a type convertible to string. If the * expectation is not met, issue a warning at the provided node's source code * position. */ void expectString(NodeTraversal t, Node n, JSType type, String msg) { if (!

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>type.matchesStringContext()) { mismatch(t, n, msg, type, STRING_TYPE); } } /** * Expect the type to be a number, or a type convertible to number. If the * expectation is not met, issue a warning at the provided node's source code * position. */ void expectNumber(NodeTraversal t, Node n, JSType type, String msg) { if (!type.matchesNumberContext()) { mismatch(t, n, msg, type, NUMBER_TYPE); } } /** * Expect the type to be a valid operand to a bitwise operator. This includes * numbers, any type convertible to a number, or any other primitive type * (undefined|null|boolean|string). */ void expectBitwiseable(NodeTraversal t, Node n, JSType type, String msg) { if (!type.matchesNumberContext() && !type.isSubtype(allValueTypes)) { mismatch(t, n, msg, type, allValueTypes); } } /** * Expect the type to be a number or string, or a type convertible to a number * or string. If the expectation is not met, issue a warning at the provided * node's source code position. */ void expectStringOrNumber( NodeTraversal t, Node n, JSType type, String msg) { if (!type.matchesNumberContext() && !type.matchesStringContext()) { mismatch(t, n, msg, type, NUMBER_STRING); } } /** * Expect the type to be anything but the null or void type. If the * expectation is not met, issue a warning at the provided node's * source code position. Note that a union type that includes the * void type and at least one other type meets the expectation. * @return Whether the expectation was met. */ boolean expectNotNullOrUndefined( NodeTraversal t, Node n, JSType type, String msg, JSType expectedType) { if (!type.isNoType() && !type.isUnknownType() && type.isSubtype(nullOrUndefined) && !containsForwardDeclaredUnresolvedName(type)) { // There's one edge case right now that we don't handle well, and // that we don't want to warn about. // if (this.x == null) { // this.initializeX(); // this.x.foo(); // } // In this case, we incorrectly type x because of how we // infer properties locally. See issue 109. // http://code.google.com/p/closure-compiler/issues/detail?id=109 // // We do not do this inference globally. if (n.isGetProp() && !t.inGlobalScope() && type.isNullType()) { return true; } mismatch

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>(t, n, msg, type, expectedType); return false; } return true; } private boolean containsForwardDeclaredUnresolvedName(JSType type) { if (type.isUnionType()) { for (JSType alt : type.toMaybeUnionType().getAlternates()) { if (containsForwardDeclaredUnresolvedName(alt)) { return true; } } } return type.isNoResolvedType(); } /** * Expect that the type of a switch condition matches the type of its * case condition. */ void expectSwitchMatchesCase(NodeTraversal t, Node n, JSType switchType, JSType caseType) { // ECMA-262, page 68, step 3 of evaluation of CaseBlock, // but allowing extra autoboxing. // TODO(user): remove extra conditions when type annotations // in the code base have adapted to the change in the compiler. if (!switchType.canTestForShallowEqualityWith(caseType) && (caseType.autoboxesTo() == null || !caseType.autoboxesTo().isSubtype(switchType))) { mismatch(t, n.getFirstChild(), "case expression doesn't match switch", caseType, switchType); } } /** * Expect that the first type can be addressed with GETELEM syntax, * and that the second type is the right type for an index into the * first type. * * @param t The node traversal. * @param n The GETELEM node to issue warnings on. * @param objType The type of the left side of the GETELEM. * @param indexType The type inside the brackets of the GETELEM. */ void expectIndexMatch(NodeTraversal t, Node n, JSType objType, JSType indexType) { Preconditions.checkState(n.isGetElem()); Node indexNode = n.getLastChild(); if (objType.isUnknownType()) { expectStringOrNumber(t, indexNode, indexType, "property access"); } else { ObjectType dereferenced = objType.dereference(); if (dereferenced != null && dereferenced.getIndexType() != null) { expectCanAssignTo(t, indexNode, indexType, dereferenced.getIndexType(), "restricted index type"); } else if (dereferenced != null && dereferenced.isArrayType()) { expectNumber(t, indexNode, indexType, "array access"); } else if (objType.matchesObjectContext()) { expectString(t, indexNode, indexType, "property access"); } else { mismatch(t, n, "only arrays or objects can be accessed", objType, typeRegistry.createUnionType(ARRAY_TYPE, OBJECT_TYPE)); } } } /** * Expect that the first

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> type can be assigned to a symbol of the second * type. * * @param t The node traversal. * @param n The node to issue warnings on. * @param rightType The type on the RHS of the assign. * @param leftType The type of the symbol on the LHS of the assign. * @param owner The owner of the property being assigned to. * @param propName The name of the property being assigned to. * @return True if the types matched, false otherwise. */ boolean expectCanAssignToPropertyOf(NodeTraversal t, Node n, JSType rightType, JSType leftType, Node owner, String propName) { // The NoType check is a hack to make typedefs work ok. if (!leftType.isNoType() && !rightType.canAssignTo(leftType)) { if (bothIntrinsics(rightType, leftType)) { // We have a superior warning for this mistake, which gives you // the line numbers of both types. registerMismatch(rightType, leftType, null); } else { mismatch(t, n, "assignment to property " + propName + " of " + getReadableJSTypeName(owner, true), rightType, leftType); } return false; } return true; } /** * Expect that the first type can be assigned to a symbol of the second * type. * * @param t The node traversal. * @param n The node to issue warnings on. * @param rightType The type on the RHS of the assign. * @param leftType The type of the symbol on the LHS of the assign. * @param msg An extra message for the mismatch warning, if necessary. * @return True if the types matched, false otherwise. */ boolean expectCanAssignTo(NodeTraversal t, Node n, JSType rightType, JSType leftType, String msg) { if (!rightType.canAssignTo(leftType)) { if (bothIntrinsics(rightType, leftType)) { // We have a superior warning for this mistake, which gives you // the line numbers of both types. registerMismatch(rightType, leftType, null); } else { mismatch(t, n, msg, rightType, leftType); } return false; } return true; } private boolean bothIntrinsics(JSType rightType, JSType leftType) { return (leftType.isConstructor() || leftType.isEnumType()) && (rightType.isConstructor() || rightType.isEnumType()); } /** * Expect that the type of an argument matches the type of the parameter * that it's fulfilling. * * @param t The node traversal. * @param n The node to issue warnings on. * @param argType The type of the argument

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>. * @param paramType The type of the parameter. * @param callNode The call node, to help with the warning message. * @param ordinal The argument ordinal, to help with the warning message. */ void expectArgumentMatchesParameter(NodeTraversal t, Node n, JSType argType, JSType paramType, Node callNode, int ordinal) { if (!argType.canAssignTo(paramType)) { mismatch(t, n, String.format("actual parameter %d of %s does not match " + "formal parameter", ordinal, getReadableJSTypeName(callNode.getFirstChild(), false)), argType, paramType); } } /** * Expect that the first type can override a property of the second * type. * * @param t The node traversal. * @param n The node to issue warnings on. * @param overridingType The overriding type. * @param hiddenType The type of the property being overridden. * @param propertyName The name of the property, for use in the * warning message. * @param ownerType The type of the owner of the property, for use * in the warning message. */ void expectCanOverride(NodeTraversal t, Node n, JSType overridingType, JSType hiddenType, String propertyName, JSType ownerType) { if (!overridingType.canAssignTo(hiddenType)) { registerMismatch(overridingType, hiddenType, report(t.makeError(n, HIDDEN_PROPERTY_MISMATCH, propertyName, ownerType.toString(), hiddenType.toString(), overridingType.toString()))); } } /** * Expect that the first type is the direct superclass of the second type. * * @param t The node traversal. * @param n The node where warnings should point to. * @param superObject The expected super instance type. * @param subObject The sub instance type. */ void expectSuperType(NodeTraversal t, Node n, ObjectType superObject, ObjectType subObject) { FunctionType subCtor = subObject.getConstructor(); ObjectType declaredSuper = subObject.getImplicitPrototype().getImplicitPrototype(); if (!declaredSuper.equals(superObject)) { if (declaredSuper.equals(getNativeType(OBJECT_TYPE))) { registerMismatch(superObject, declaredSuper, report( t.makeError(n, MISSING_EXTENDS_TAG_WARNING, subObject.toString()))); } else { mismatch(t.getSourceName(), n, "mismatch in declaration of superclass type", superObject, declaredSuper); } // Correct the super type. if (!subCtor.hasCachedValues()) { subCtor.setPrototypeBasedOn(superObject); } } } /** * Expect that the first type can be cast to the second type. The first type * should

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> be either a subtype or supertype of the second. * * @param t The node traversal. * @param n The node where warnings should point. * @param type The type being cast from. * @param castType The type being cast to. */ void expectCanCast(NodeTraversal t, Node n, JSType type, JSType castType) { castType = castType.restrictByNotNullOrUndefined(); type = type.restrictByNotNullOrUndefined(); if (!type.canAssignTo(castType) && !castType.canAssignTo(type)) { registerMismatch(type, castType, report(t.makeError(n, INVALID_CAST, castType.toString(), type.toString()))); } } /** * Expect that the given variable has not been declared with a type. * * @param sourceName The name of the source file we're in. * @param n The node where warnings should point to. * @param parent The parent of {@code n}. * @param var The variable that we're checking. * @param variableName The name of the variable. * @param newType The type being applied to the variable. Mostly just here * for the benefit of the warning. */ void expectUndeclaredVariable(String sourceName, CompilerInput input, Node n, Node parent, Var var, String variableName, JSType newType) { boolean allowDupe = false; if (n.isGetProp() || NodeUtil.isObjectLitKey(n, parent)) { JSDocInfo info = n.getJSDocInfo(); if (info == null) { info = parent.getJSDocInfo(); } allowDupe = info != null && info.getSuppressions().contains("duplicate"); } JSType varType = var.getType(); // Only report duplicate declarations that have types. Other duplicates // will be reported by the syntactic scope creator later in the // compilation process. if (varType != null && varType != typeRegistry.getNativeType(UNKNOWN_TYPE) && newType != null && newType != typeRegistry.getNativeType(UNKNOWN_TYPE)) { // If there are two typed declarations of the same variable, that // is an error and the second declaration is ignored, except in the // case of native types. A null input type means that the declaration // was made in TypedScopeCreator#createInitialScope and is a // native type. We should redeclare it at the new input site. if (var.input == null) { Scope s = var.getScope(); s.undeclare(var); s.declare(variableName, n, varType, input, false); n.setJSType(varType); if (parent.isVar()) { if (n.getFirstChild() != null) {

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> n.getFirstChild().setJSType(varType); } } else { Preconditions.checkState(parent.isFunction()); parent.setJSType(varType); } } else { // Always warn about duplicates if the overridden type does not // match the original type. // // If the types match, suppress the warning iff there was a @suppress // tag, or if the original declaration was a stub. if (!(allowDupe || var.getParentNode().isExprResult()) || !newType.equals(varType)) { report(JSError.make(sourceName, n, DUP_VAR_DECLARATION, variableName, newType.toString(), var.getInputName(), String.valueOf(var.nameNode.getLineno()), varType.toString())); } } } } /** * Expect that all properties on interfaces that this type implements are * implemented and correctly typed. */ void expectAllInterfaceProperties(NodeTraversal t, Node n, FunctionType type) { ObjectType instance = type.getInstanceType(); for (ObjectType implemented : type.getAllImplementedInterfaces()) { if (implemented.getImplicitPrototype() != null) { for (String prop : implemented.getImplicitPrototype().getOwnPropertyNames()) { expectInterfaceProperty(t, n, instance, implemented, prop); } } } } /** * Expect that the peroperty in an interface that this type implements is * implemented and correctly typed. */ private void expectInterfaceProperty(NodeTraversal t, Node n, ObjectType instance, ObjectType implementedInterface, String prop) { if (!instance.hasProperty(prop)) { // Not implemented String sourceName = n.getSourceFileName(); sourceName = sourceName == null ? "" : sourceName; registerMismatch(instance, implementedInterface, report(JSError.make(sourceName, n, INTERFACE_METHOD_NOT_IMPLEMENTED, prop, implementedInterface.toString(), instance.toString()))); } else { JSType found = instance.getPropertyType(prop); JSType required = implementedInterface.getImplicitPrototype().getPropertyType(prop); found = found.restrictByNotNullOrUndefined(); required = required.restrictByNotNullOrUndefined(); if (!found.canAssignTo(required)) { // Implemented, but not correctly typed FunctionType constructor = implementedInterface.toObjectType().getConstructor(); registerMismatch(found, required, report(t.makeError(n, HIDDEN_INTERFACE_PROPERTY_MISMATCH, prop, constructor.getTopMostDefiningType(prop).toString(), required.toString(), found.toString()))); } } } /** * Report a type mismatch */ private void mismatch(NodeTraversal t, Node n, String msg, JSType found, JSType required) { mismatch(t.getSourceName(), n, msg, found, required);

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> } private void mismatch(NodeTraversal t, Node n, String msg, JSType found, JSTypeNative required) { mismatch(t, n, msg, found, getNativeType(required)); } private void mismatch(String sourceName, Node n, String msg, JSType found, JSType required) { registerMismatch(found, required, report( JSError.make(sourceName, n, TYPE_MISMATCH_WARNING, formatFoundRequired(msg, found, required)))); } private void registerMismatch(JSType found, JSType required, JSError error) { // Don't register a mismatch for differences in null or undefined or if the // code didn't downcast. found = found.restrictByNotNullOrUndefined(); required = required.restrictByNotNullOrUndefined(); if (found.canAssignTo(required) || required.canAssignTo(found)) { return; } mismatches.add(new TypeMismatch(found, required, error)); if (found.isFunctionType() && required.isFunctionType()) { FunctionType fnTypeA = found.toMaybeFunctionType(); FunctionType fnTypeB = required.toMaybeFunctionType(); Iterator<Node> paramItA = fnTypeA.getParameters().iterator(); Iterator<Node> paramItB = fnTypeB.getParameters().iterator(); while (paramItA.hasNext() && paramItB.hasNext()) { registerIfMismatch(paramItA.next().getJSType(), paramItB.next().getJSType(), error); } registerIfMismatch( fnTypeA.getReturnType(), fnTypeB.getReturnType(), error); } } private void registerIfMismatch( JSType found, JSType required, JSError error) { if (found != null && required != null && !found.canAssignTo(required)) { registerMismatch(found, required, error); } } /** * Formats a found/required error message. */ private String formatFoundRequired(String description, JSType found, JSType required) { return MessageFormat.format(FOUND_REQUIRED, description, found, required); } /** * Given a node, get a human-readable name for the type of that node so * that will be easy for the programmer to find the original declaration. * * For example, if SubFoo's property "bar" might have the human-readable * name "Foo.prototype.bar". * * @param n The node. * @param dereference If true, the type of the node will be dereferenced * to an Object type, if possible. */ String getReadableJSTypeName(Node n, boolean dereference) { // If we're analyzing a GETPROP, the property may be inherited by the // prototype chain. So climb the prototype chain and find out where // the property was

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> originally defined. if (n.isGetProp()) { ObjectType objectType = getJSType(n.getFirstChild()).dereference(); if (objectType != null) { String propName = n.getLastChild().getString(); if (objectType.getConstructor() != null && objectType.getConstructor().isInterface()) { objectType = FunctionType.getTopDefiningInterface( objectType, propName); } else { // classes while (objectType != null && !objectType.hasOwnProperty(propName)) { objectType = objectType.getImplicitPrototype(); } } // Don't show complex function names or anonymous types. // Instead, try to get a human-readable type name. if (objectType != null && (objectType.getConstructor() != null || objectType.isFunctionPrototypeType())) { return objectType.toString() + "." + propName; } } } JSType type = getJSType(n); if (dereference) { ObjectType dereferenced = type.dereference(); if (dereferenced != null) { type = dereferenced; } } String qualifiedName = n.getQualifiedName(); if (type.isFunctionPrototypeType() || (type.toObjectType() != null && type.toObjectType().getConstructor() != null)) { return type.toString(); } else if (qualifiedName != null) { return qualifiedName; } else if (type.isFunctionType()) { // Don't show complex function names. return "function"; } else { return type.toString(); } } /** * This method gets the JSType from the Node argument and verifies that it is * present. */ private JSType getJSType(Node n) { JSType jsType = n.getJSType(); if (jsType == null) { // TODO(user): This branch indicates a compiler bug, not worthy of // halting the compilation but we should log this and analyze to track // down why it happens. This is not critical and will be resolved over // time as the type checker is extended. return getNativeType(UNKNOWN_TYPE); } else { return jsType; } } private JSType getNativeType(JSTypeNative typeId) { return typeRegistry.getNativeType(typeId); } private JSError report(JSError error) { if (shouldReport) { compiler.report(error); } return error; } /** * Signals that the first type and the second type have been * used interchangeably. * * Type-based optimizations should take this into account * so that they don't wreck code with type warnings. */ static class TypeMismatch { final JSType typeA; final JSType typeB; final JSError src;

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.isFunction()) { // Don't traverse functions that are constructors or have the @this // or @override annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || pType == Token.ASSIGN || // object literal keys pType == Token.STRING)) { return false; } // Don't traverse functions that are getting lent to a prototype. Node gramps = parent.getParent(); if (NodeUtil.isObjectLitKey(parent, gramps)) { JSDocInfo maybeLends = gramps.getJSDocInfo(); if (maybeLends != null && maybeLends.getLendsName() != null && maybeLends.getLendsName().endsWith(".prototype")) { return false; } } } if (parent != null && parent.isAssign()) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { // Only traverse the right side if it's not an assignment to a prototype // property or subproperty. if (NodeUtil.isGet(lhs)) { if (lhs.isGetProp() && lhs.getLastChild().getString().equals("prototype")) { return false; } Node llhs = lhs.getFirstChild(); if (llhs.isGetProp() && llhs.getLastChild().getString().equals("prototype")) { return false; } } } } return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isThis() && shouldReportThis(n, parent)) { compiler.report

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>(t.makeError(n, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. return parent != null && NodeUtil.isGet(parent); } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} * ... x = function() {}; * var ... x = function() {}; * ... var x = function() {}; * </pre> */ private JSDocInfo getFunctionJsDocInfo(Node n) { JSDocInfo jsDoc = n.getJSDocInfo(); Node parent = n.getParent(); if (jsDoc == null) { int parentType = parent.getType(); if (parentType == Token.NAME || parentType == Token.ASSIGN) { jsDoc = parent.getJSDocInfo(); if (jsDoc == null && parentType == Token.NAME) { Node gramps = parent.getParent(); if (gramps.isVar()) { jsDoc = gramps.getJSDocInfo(); } } } } return jsDoc; } }

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>removeAll(allDefines.keySet()); unusedReplacements.removeAll(KNOWN_DEFINES); for (String unknownDefine : unusedReplacements) { compiler.report(JSError.make(UNKNOWN_DEFINE_WARNING, unknownDefine)); } } private static String format(MessageFormat format, Object... params) { return format.format(params); } /** * Only defines of literal number, string, or boolean are supported. */ private boolean isValidDefineType(JSTypeExpression expression) { JSType type = expression.evaluate(null, compiler.getTypeRegistry()); return !type.isUnknownType() && type.isSubtype( compiler.getTypeRegistry().getNativeType( JSTypeNative.NUMBER_STRING_BOOLEAN)); } /** * Finds all defines, and creates a {@link DefineInfo} data structure for * each one. * @return A map of {@link DefineInfo} structures, keyed by name. */ private Map<String, DefineInfo> collectDefines(Node root, GlobalNamespace namespace) { // Find all the global names with a @define annotation List<Name> allDefines = Lists.newArrayList(); for (Name name : namespace.getNameIndex().values()) { Ref decl = name.getDeclaration(); if (name.docInfo != null && name.docInfo.isDefine()) { // Process defines should not depend on check types being enabled, // so we look for the JSDoc instead of the inferred type. if (isValidDefineType(name.docInfo.getType())) { allDefines.add(name); } else { JSError error = JSError.make( decl.getSourceName(), decl.node, INVALID_DEFINE_TYPE_ERROR); compiler.report(error); } } else { for (Ref ref : name.getRefs()) { if (ref == decl) { // Declarations were handled above. continue; } Node n = ref.node; Node parent = ref.node.getParent(); JSDocInfo info = n.getJSDocInfo(); if (info == null && parent.isVar() && parent.hasOneChild()) { info = parent.getJSDocInfo(); } if (info != null && info.isDefine()) { allDefines.add(name); break; } } } } CollectDefines pass = new CollectDefines(compiler, allDefines); NodeTraversal.traverse(compiler, root, pass); return pass.getAllDefines(); } /** * Finds all assignments to @defines, and figures out the last value of * the @define. */ private static final class CollectDefines implements Callback { private final AbstractCompiler compiler; private final Map<String, DefineInfo> assignableDefines; private final Map<String, DefineInfo> allDefines; private final Map<Node, RefInfo> allRefInfo

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>; // A hack that allows us to remove ASSIGN/VAR statements when // we're currently visiting one of the children of the assign. private Node lvalueToRemoveLater = null; // A stack tied to the node traversal, to keep track of whether // we're in a conditional block. If 1 is at the top, assignment to // a define is allowed. Otherwise, it's not allowed. private final Deque<Integer> assignAllowed; CollectDefines(AbstractCompiler compiler, List<Name> listOfDefines) { this.compiler = compiler; this.allDefines = Maps.newHashMap(); assignableDefines = Maps.newHashMap(); assignAllowed = new ArrayDeque<Integer>(); assignAllowed.push(1); // Create a map of references to defines keyed by node for easy lookup allRefInfo = Maps.newHashMap(); for (Name name : listOfDefines) { Ref decl = name.getDeclaration(); if (decl != null) { allRefInfo.put(decl.node, new RefInfo(decl, name)); } for (Ref ref : name.getRefs()) { if (ref == decl) { // Declarations were handled above. continue; } // If there's a TWIN def, only put one of the twins in. if (ref.getTwin() == null || !ref.getTwin().isSet()) { allRefInfo.put(ref.node, new RefInfo(ref, name)); } } } } /** * Get a map of {@link DefineInfo} structures, keyed by the name of * the define. */ Map<String, DefineInfo> getAllDefines() { return allDefines; } /** * Keeps track of whether the traversal is in a conditional branch. * We traverse all nodes of the parse tree. */ @Override public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { updateAssignAllowedStack(n, true); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { RefInfo refInfo = allRefInfo.get(n); if (refInfo != null) { Ref ref = refInfo.ref; Name name = refInfo.name; String fullName = name.getFullName(); switch (ref.type) { case SET_FROM_GLOBAL: case SET_FROM_LOCAL: Node valParent = getValueParent(ref); Node val = valParent.getLastChild(); if (valParent.isAssign() && name.isSimpleName() && name.getDeclaration() == ref) { // For defines, it's an error if a simple name is assigned // before it's declared compiler.report( t.makeError(val, INVALID_DEFINE_INIT_ERROR, fullName)); } else if (process

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>DefineAssignment(t, fullName, val, valParent)) { // remove the assignment so that the variable is still declared, // but no longer assigned to a value, e.g., // DEF_FOO = 5; // becomes "5;" // We can't remove the ASSIGN/VAR when we're still visiting its // children, so we'll have to come back later to remove it. refInfo.name.removeRef(ref); lvalueToRemoveLater = valParent; } break; default: if (t.inGlobalScope()) { // Treat this as a reference to a define in the global scope. // After this point, the define must not be reassigned, // or it's an error. DefineInfo info = assignableDefines.get(fullName); if (info != null) { setDefineInfoNotAssignable(info, t); assignableDefines.remove(fullName); } } break; } } if (!t.inGlobalScope() && n.getJSDocInfo() != null && n.getJSDocInfo().isDefine()) { // warn about @define annotations in local scopes compiler.report( t.makeError(n, NON_GLOBAL_DEFINE_INIT_ERROR, "")); } if (lvalueToRemoveLater == n) { lvalueToRemoveLater = null; if (n.isAssign()) { Node last = n.getLastChild(); n.removeChild(last); parent.replaceChild(n, last); } else { Preconditions.checkState(n.isName()); n.removeChild(n.getFirstChild()); } compiler.reportCodeChange(); } if (n.isCall()) { if (t.inGlobalScope()) { // If there's a function call in the global scope, // we just say it's unsafe and freeze all the defines. // // NOTE(nicksantos): We could be a lot smarter here. For example, // ReplaceOverriddenVars keeps a call graph of all functions and // which functions/variables that they reference, and tries // to statically determine which functions are "safe" and which // are not. But this would be overkill, expecially because // the intended use of defines is with config_files, where // all the defines are at the top of the bundle. for (DefineInfo info : assignableDefines.values()) { setDefineInfoNotAssignable(info, t); } assignableDefines.clear(); } } updateAssignAllowedStack(n, false); } /** * Determines whether assignment to a define should be allowed * in the subtree of the given node, and if not, records that fact. * * @param n The node whose subtree we're about to enter or exit. * @param entering True if we're entering the subtree, false otherwise.

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> */ private void updateAssignAllowedStack(Node n, boolean entering) { switch (n.getType()) { case Token.CASE: case Token.FOR: case Token.FUNCTION: case Token.HOOK: case Token.IF: case Token.SWITCH: case Token.WHILE: if (entering) { assignAllowed.push(0); } else { assignAllowed.remove(); } break; } } /** * Determines whether assignment to a define should be allowed * at the current point of the traversal. */ private boolean isAssignAllowed() { return assignAllowed.element() == 1; } /** * Tracks the given define. * * @param t The current traversal, for context. * @param name The full name for this define. * @param value The value assigned to the define. * @param valueParent The parent node of value. * @return Whether we should remove this assignment from the parse tree. */ private boolean processDefineAssignment(NodeTraversal t, String name, Node value, Node valueParent) { if (value == null || !NodeUtil.isValidDefineValue(value, allDefines.keySet())) { compiler.report( t.makeError(value, INVALID_DEFINE_INIT_ERROR, name)); } else if (!isAssignAllowed()) { compiler.report( t.makeError(valueParent, NON_GLOBAL_DEFINE_INIT_ERROR, name)); } else { DefineInfo info = allDefines.get(name); if (info == null) { // First declaration of this define. info = new DefineInfo(value, valueParent); allDefines.put(name, info); assignableDefines.put(name, info); } else if (info.recordAssignment(value)) { // The define was already initialized, but this is a safe // re-assignment. return true; } else { // The define was already initialized, and this is an unsafe // re-assignment. compiler.report( t.makeError(valueParent, DEFINE_NOT_ASSIGNABLE_ERROR, name, info.getReasonWhyNotAssignable())); } } return false; } /** * Gets the parent node of the value for any assignment to a Name. * For example, in the assignment * {@code var x = 3;} * the parent would be the NAME node. */ private static Node getValueParent(Ref ref) { // there are two types of declarations: VARs and ASSIGNs return ref.node.getParent() != null && ref.node.getParent().isVar() ? ref.node : ref.node.getParent(); } /** * Records the fact that because of the current node in the node traversal, * the define can't ever be assigned again. * * @param info

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>.google.javascript.rhino.jstype.StaticSlot; import java.util.Iterator; import java.util.List; import java.util.Map; /** * Type inference within a script node or a function body, using the data-flow * analysis framework. * */ class TypeInference extends DataFlowAnalysis.BranchedForwardDataFlowAnalysis<Node, FlowScope> { static final DiagnosticType TEMPLATE_TYPE_NOT_OBJECT_TYPE = DiagnosticType.warning( "JSC_TEMPLATE_TYPE_NOT_OBJECT_TYPE", "The template type must be an object type.\nActual: {0}"); static final DiagnosticType TEMPLATE_TYPE_OF_THIS_EXPECTED = DiagnosticType.warning( "JSC_TEMPLATE_TYPE_OF_THIS_EXPECTED", "A function type with the template type as the type of this must be a " + "parameter type"); static final DiagnosticType FUNCTION_LITERAL_UNDEFINED_THIS = DiagnosticType.warning( "JSC_FUNCTION_LITERAL_UNDEFINED_THIS", "Function literal argument refers to undefined this argument"); private final AbstractCompiler compiler; private final JSTypeRegistry registry; private final ReverseAbstractInterpreter reverseInterpreter; private final Scope syntacticScope; private final FlowScope functionScope; private final FlowScope bottomScope; private final Map<String, AssertionFunctionSpec> assertionFunctionsMap; TypeInference(AbstractCompiler compiler, ControlFlowGraph<Node> cfg, ReverseAbstractInterpreter reverseInterpreter, Scope functionScope, Map<String, AssertionFunctionSpec> assertionFunctionsMap) { super(cfg, new LinkedFlowScope.FlowScopeJoinOp()); this.compiler = compiler; this.registry = compiler.getTypeRegistry(); this.reverseInterpreter = reverseInterpreter; this.syntacticScope = functionScope; this.functionScope = LinkedFlowScope.createEntryLattice(functionScope); this.assertionFunctionsMap = assertionFunctionsMap; // For each local variable declared with the VAR keyword, the entry // type is VOID. Iterator<Var> varIt = functionScope.getDeclarativelyUnboundVarsWithoutTypes(); while (varIt.hasNext()) { Var var = varIt.next(); if (isUnflowable(var)) { continue; } this.functionScope.inferSlotType( var.getName(), getNativeType(VOID_TYPE)); } this.bottomScope = LinkedFlowScope.createEntryLattice( new Scope(functionScope.getRootNode(), functionScope.getTypeOfThis())); } @Override FlowScope createInitialEstimateLattice() { return bottomScope; } @Override FlowScope createEntryLattice() { return functionScope; } @Override FlowScope flowThrough(Node n, FlowScope input) { // If we have not walked a path from <entry> to <n>,

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> then we don't // want to infer anything about this scope. if (input == bottomScope) { return input; } FlowScope output = input.createChildFlowScope(); output = traverse(n, output); return output; } @Override @SuppressWarnings("fallthrough") List<FlowScope> branchedFlowThrough(Node source, FlowScope input) { // NOTE(nicksantos): Right now, we just treat ON_EX edges like UNCOND // edges. If we wanted to be perfect, we'd actually JOIN all the out // lattices of this flow with the in lattice, and then make that the out // lattice for the ON_EX edge. But it's probably to expensive to be // worthwhile. FlowScope output = flowThrough(source, input); Node condition = null; FlowScope conditionFlowScope = null; BooleanOutcomePair conditionOutcomes = null; List<DiGraphEdge<Node, Branch>> branchEdges = getCfg().getOutEdges(source); List<FlowScope> result = Lists.newArrayListWithCapacity(branchEdges.size()); for (DiGraphEdge<Node, Branch> branchEdge : branchEdges) { Branch branch = branchEdge.getValue(); FlowScope newScope = output; switch (branch) { case ON_TRUE: if (NodeUtil.isForIn(source)) { // item is assigned a property name, so its type should be string. Node item = source.getFirstChild(); Node obj = item.getNext(); FlowScope informed = traverse(obj, output.createChildFlowScope()); if (item.isVar()) { item = item.getFirstChild(); } if (item.isName()) { JSType iterKeyType = getNativeType(STRING_TYPE); ObjectType objType = getJSType(obj).dereference(); JSType objIndexType = objType == null ? null : objType.getIndexType(); if (objIndexType != null && !objIndexType.isUnknownType()) { JSType narrowedKeyType = iterKeyType.getGreatestSubtype(objIndexType); if (!narrowedKeyType.isEmptyType()) { iterKeyType = narrowedKeyType; } } redeclareSimpleVar(informed, item, iterKeyType); } newScope = informed; break; } // FALL THROUGH case ON_FALSE: if (condition == null) { condition = NodeUtil.getConditionExpression(source); if (condition == null && source.isCase()) { condition = source; // conditionFlowScope is cached from previous iterations // of the loop. if (conditionFlowScope == null) { conditionFlowScope = traverse( condition.getFirstChild(), output.createChildFlowScope()); } } } if (condition != null) { if (condition.isAnd()

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> || condition.isOr()) { // When handling the short-circuiting binary operators, // the outcome scope on true can be different than the outcome // scope on false. // // TODO(nicksantos): The "right" way to do this is to // carry the known outcome all the way through the // recursive traversal, so that we can construct a // different flow scope based on the outcome. However, // this would require a bunch of code and a bunch of // extra computation for an edge case. This seems to be // a "good enough" approximation. // conditionOutcomes is cached from previous iterations // of the loop. if (conditionOutcomes == null) { conditionOutcomes = condition.isAnd() ? traverseAnd(condition, output.createChildFlowScope()) : traverseOr(condition, output.createChildFlowScope()); } newScope = reverseInterpreter.getPreciserScopeKnowingConditionOutcome( condition, conditionOutcomes.getOutcomeFlowScope( condition.getType(), branch == Branch.ON_TRUE), branch == Branch.ON_TRUE); } else { // conditionFlowScope is cached from previous iterations // of the loop. if (conditionFlowScope == null) { conditionFlowScope = traverse(condition, output.createChildFlowScope()); } newScope = reverseInterpreter.getPreciserScopeKnowingConditionOutcome( condition, conditionFlowScope, branch == Branch.ON_TRUE); } } break; } result.add(newScope.optimize()); } return result; } private FlowScope traverse(Node n, FlowScope scope) { switch (n.getType()) { case Token.ASSIGN: scope = traverseAssign(n, scope); break; case Token.NAME: scope = traverseName(n, scope); break; case Token.GETPROP: scope = traverseGetProp(n, scope); break; case Token.AND: scope = traverseAnd(n, scope).getJoinedFlowScope() .createChildFlowScope(); break; case Token.OR: scope = traverseOr(n, scope).getJoinedFlowScope() .createChildFlowScope(); break; case Token.HOOK: scope = traverseHook(n, scope); break; case Token.OBJECTLIT: scope = traverseObjectLiteral(n, scope); break; case Token.CALL: scope = traverseCall(n, scope); break; case Token.NEW: scope = traverseNew(n, scope); break; case Token.ASSIGN_ADD: case Token.ADD: scope = traverseAdd(n, scope); break; case Token.POS: case Token.NEG: scope = traverse(n.getFirstChild(), scope); // Find types. n.setJSType(get

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>NativeType(NUMBER_TYPE)); break; case Token.ARRAYLIT: scope = traverseArrayLiteral(n, scope); break; case Token.THIS: n.setJSType(scope.getTypeOfThis()); break; case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.LSH: case Token.RSH: case Token.ASSIGN_URSH: case Token.URSH: case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: case Token.ASSIGN_BITAND: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITOR: case Token.ASSIGN_MUL: case Token.ASSIGN_SUB: case Token.DIV: case Token.MOD: case Token.BITAND: case Token.BITXOR: case Token.BITOR: case Token.MUL: case Token.SUB: case Token.DEC: case Token.INC: case Token.BITNOT: scope = traverseChildren(n, scope); n.setJSType(getNativeType(NUMBER_TYPE)); break; case Token.PARAM_LIST: scope = traverse(n.getFirstChild(), scope); n.setJSType(getJSType(n.getFirstChild())); break; case Token.COMMA: scope = traverseChildren(n, scope); n.setJSType(getJSType(n.getLastChild())); break; case Token.TYPEOF: scope = traverseChildren(n, scope); n.setJSType(getNativeType(STRING_TYPE)); break; case Token.DELPROP: case Token.LT: case Token.LE: case Token.GT: case Token.GE: case Token.NOT: case Token.EQ: case Token.NE: case Token.SHEQ: case Token.SHNE: case Token.INSTANCEOF: case Token.IN: scope = traverseChildren(n, scope); n.setJSType(getNativeType(BOOLEAN_TYPE)); break; case Token.GETELEM: scope = traverseGetElem(n, scope); break; case Token.EXPR_RESULT: scope = traverseChildren(n, scope); if (n.getFirstChild().isGetProp()) { ensurePropertyDeclared(n.getFirstChild()); } break; case Token.SWITCH: scope = traverse(n.getFirstChild(), scope); break; case Token.VAR: case Token.RETURN: case Token.THROW: scope = traverseChildren(n, scope); break; case Token.CATCH: scope = traverseCatch(n, scope); break; } if (n.getType() != Token.FUNCTION) { JSDocInfo info = n.getJSDocInfo(); if (info != null && info.

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>hasType()) { JSType castType = info.getType().evaluate(syntacticScope, registry); // A stubbed type cast on a qualified name should take // effect for all subsequent accesses of that name, // so treat it the same as an assign to that name. if (n.isQualifiedName() && n.getParent().isExprResult()) { updateScopeForTypeChange(scope, n, n.getJSType(), castType); } n.setJSType(castType); } } return scope; } /** * Any value can be thrown, so it's really impossible to determine the type * of a CATCH param. Treat it as the UNKNOWN type. */ private FlowScope traverseCatch(Node n, FlowScope scope) { Node name = n.getFirstChild(); JSType type = getNativeType(JSTypeNative.UNKNOWN_TYPE); name.setJSType(type); redeclareSimpleVar(scope, name, type); return scope; } private FlowScope traverseAssign(Node n, FlowScope scope) { Node left = n.getFirstChild(); Node right = n.getLastChild(); scope = traverseChildren(n, scope); JSType leftType = left.getJSType(); JSType rightType = getJSType(right); n.setJSType(rightType); updateScopeForTypeChange(scope, left, leftType, rightType); return scope; } /** * Updates the scope according to the result of a type change, like * an assignment or a type cast. */ private void updateScopeForTypeChange( FlowScope scope, Node left, JSType leftType, JSType resultType) { Preconditions.checkNotNull(resultType); switch (left.getType()) { case Token.NAME: String varName = left.getString(); Var var = syntacticScope.getVar(varName); // When looking at VAR initializers for declared VARs, we trust // the declared type over the type it's being initialized to. // This has two purposes: // 1) We avoid re-declaring declared variables so that built-in // types defined in externs are not redeclared. // 2) When there's a lexical closure like // /** @type {?string} */ var x = null; // function f() { x = 'xyz'; } // the inference will ignore the lexical closure, // which is just wrong. This bug needs to be fixed eventually. boolean isVarDeclaration = left.hasChildren(); if (!isVarDeclaration || var == null || var.isTypeInferred()) { redeclareSimpleVar(scope, left, resultType); } left.setJSType(isVarDeclaration || leftType == null ? resultType : null); if (var != null && var.isTypeInferred()) { JSType

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>ScopeCreator}. */ private void ensurePropertyDeclared(Node getprop) { ObjectType ownerType = ObjectType.cast( getJSType(getprop.getFirstChild()).restrictByNotNullOrUndefined()); if (ownerType != null) { ensurePropertyDeclaredHelper(getprop, ownerType); } } /** * Declares a property on its owner, if necessary. * @return True if a property was declared. */ private boolean ensurePropertyDeclaredHelper( Node getprop, ObjectType objectType) { String propName = getprop.getLastChild().getString(); String qName = getprop.getQualifiedName(); if (qName != null) { Var var = syntacticScope.getVar(qName); if (var != null && !var.isTypeInferred()) { // Handle normal declarations that could not be addressed earlier. if (propName.equals("prototype") || // Handle prototype declarations that could not be addressed earlier. (!objectType.hasOwnProperty(propName) && (!objectType.isInstanceType() || (var.isExtern() && !objectType.isNativeObjectType())))) { return objectType.defineDeclaredProperty( propName, var.getType(), getprop); } } } return false; } private FlowScope traverseName(Node n, FlowScope scope) { String varName = n.getString(); Node value = n.getFirstChild(); JSType type = n.getJSType(); if (value != null) { scope = traverse(value, scope); updateScopeForTypeChange(scope, n, n.getJSType() /* could be null */, getJSType(value)); return scope; } else { StaticSlot<JSType> var = scope.getSlot(varName); if (var != null) { // There are two situations where we don't want to use type information // from the scope, even if we have it. // 1) The var is escaped in a weird way, e.g., // function f() { var x = 3; function g() { x = null } (x); } boolean isInferred = var.isTypeInferred(); boolean unflowable = isInferred && isUnflowable(syntacticScope.getVar(varName)); // 2) We're reading type information from another scope for an // inferred variable. // var t = null; function f() { (t); } boolean nonLocalInferredSlot = isInferred && syntacticScope.getParent() != null && var == syntacticScope.getParent().getSlot(varName); if (!unflowable && !nonLocalInferredSlot) { type = var.getType(); if (type == null) { type = getNativeType(UNKNOWN_TYPE); } } } } n.setJSType(type); return scope;

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> } /** Traverse each element of the array. */ private FlowScope traverseArrayLiteral(Node n, FlowScope scope) { scope = traverseChildren(n, scope); n.setJSType(getNativeType(ARRAY_TYPE)); return scope; } private FlowScope traverseObjectLiteral(Node n, FlowScope scope) { JSType type = n.getJSType(); Preconditions.checkNotNull(type); for (Node name = n.getFirstChild(); name != null; name = name.getNext()) { scope = traverse(name.getFirstChild(), scope); } // Object literals can be reflected on other types, or changed with // type casts. // See CodingConvention#getObjectLiteralCase and goog.object.reflect. // Ignore these types of literals. // TODO(nicksantos): There should be an "anonymous object" type that // we can check for here. ObjectType objectType = ObjectType.cast(type); if (objectType == null) { return scope; } boolean hasLendsName = n.getJSDocInfo() != null && n.getJSDocInfo().getLendsName() != null; if (objectType.hasReferenceName() && !hasLendsName) { return scope; } for (Node name = n.getFirstChild(); name != null; name = name.getNext()) { Node value = name.getFirstChild(); String memberName = NodeUtil.getObjectLitKeyName(name); if (memberName != null) { JSType rawValueType = name.getFirstChild().getJSType(); JSType valueType = NodeUtil.getObjectLitKeyTypeFromValueType( name, rawValueType); if (valueType == null) { valueType = getNativeType(UNKNOWN_TYPE); } objectType.defineInferredProperty(memberName, valueType, name); } else { n.setJSType(getNativeType(UNKNOWN_TYPE)); } } return scope; } private FlowScope traverseAdd(Node n, FlowScope scope) { Node left = n.getFirstChild(); Node right = left.getNext(); scope = traverseChildren(n, scope); JSType leftType = left.getJSType(); JSType rightType = right.getJSType(); JSType type = getNativeType(UNKNOWN_TYPE); if (leftType != null && rightType != null) { boolean leftIsUnknown = leftType.isUnknownType(); boolean rightIsUnknown = rightType.isUnknownType(); if (leftIsUnknown && rightIsUnknown) { type = getNativeType(UNKNOWN_TYPE); } else if ((!leftIsUnknown && leftType.isString()) || (!rightIsUnknown && rightType.isString())) { type = getNativeType(STRING_TYPE); } else if (leftIsUnknown || rightIsUnknown) { type

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> = getNativeType(UNKNOWN_TYPE); } else if (isAddedAsNumber(leftType) && isAddedAsNumber(rightType)) { type = getNativeType(NUMBER_TYPE); } else { type = registry.createUnionType(STRING_TYPE, NUMBER_TYPE); } } n.setJSType(type); if (n.getType() == Token.ASSIGN_ADD) { updateScopeForTypeChange(scope, left, leftType, type); } return scope; } private boolean isAddedAsNumber(JSType type) { return type.isSubtype(registry.createUnionType(VOID_TYPE, NULL_TYPE, NUMBER_VALUE_OR_OBJECT_TYPE, BOOLEAN_TYPE, BOOLEAN_OBJECT_TYPE)); } private FlowScope traverseHook(Node n, FlowScope scope) { Node condition = n.getFirstChild(); Node trueNode = condition.getNext(); Node falseNode = n.getLastChild(); // verify the condition scope = traverse(condition, scope); // reverse abstract interpret the condition to produce two new scopes FlowScope trueScope = reverseInterpreter. getPreciserScopeKnowingConditionOutcome( condition, scope, true); FlowScope falseScope = reverseInterpreter. getPreciserScopeKnowingConditionOutcome( condition, scope, false); // traverse the true node with the trueScope traverse(trueNode, trueScope.createChildFlowScope()); // traverse the false node with the falseScope traverse(falseNode, falseScope.createChildFlowScope()); // meet true and false nodes' types and assign JSType trueType = trueNode.getJSType(); JSType falseType = falseNode.getJSType(); if (trueType != null && falseType != null) { n.setJSType(trueType.getLeastSupertype(falseType)); } else { n.setJSType(null); } return scope.createChildFlowScope(); } private FlowScope traverseCall(Node n, FlowScope scope) { scope = traverseChildren(n, scope); Node left = n.getFirstChild(); JSType functionType = getJSType(left).restrictByNotNullOrUndefined(); if (functionType != null) { if (functionType.isFunctionType()) { FunctionType fnType = functionType.toMaybeFunctionType(); n.setJSType(fnType.getReturnType()); updateTypeOfParameters(n, fnType); updateTypeOfThisOnClosure(n, fnType); } else if (functionType.equals(getNativeType(CHECKED_UNKNOWN_TYPE))) { n.setJSType(getNativeType(CHECKED_UNKNOWN_TYPE)); } } scope = tightenTypesAfterAssertions(scope, n); return scope; } private FlowScope tightenTypesAfterAssertions(FlowScope scope, Node callNode) { Node

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> left = callNode.getFirstChild(); Node firstParam = left.getNext(); AssertionFunctionSpec assertionFunctionSpec = assertionFunctionsMap.get(left.getQualifiedName()); if (assertionFunctionSpec == null || firstParam == null) { return scope; } Node assertedNode = assertionFunctionSpec.getAssertedParam(firstParam); if (assertedNode == null) { return scope; } JSTypeNative assertedType = assertionFunctionSpec.getAssertedType(); String assertedNodeName = assertedNode.getQualifiedName(); // Handle assertions that enforce expressions evaluate to true. if (assertedType == null) { if (assertedNodeName != null) { JSType type = getJSType(assertedNode); JSType narrowed = type.restrictByNotNullOrUndefined(); if (type != narrowed) { scope = narrowScope(scope, assertedNode, narrowed); callNode.setJSType(narrowed); } } else if (assertedNode.isAnd() || assertedNode.isOr()) { BooleanOutcomePair conditionOutcomes = traverseWithinShortCircuitingBinOp(assertedNode, scope); scope = reverseInterpreter.getPreciserScopeKnowingConditionOutcome( assertedNode, conditionOutcomes.getOutcomeFlowScope( assertedNode.getType(), true), true); } } else if (assertedNodeName != null) { // Handle assertions that enforce expressions are of a certain type. JSType type = getJSType(assertedNode); JSType narrowed = type.getGreatestSubtype(getNativeType(assertedType)); if (type != narrowed) { scope = narrowScope(scope, assertedNode, narrowed); callNode.setJSType(narrowed); } } return scope; } private FlowScope narrowScope(FlowScope scope, Node node, JSType narrowed) { scope = scope.createChildFlowScope(); if (node.isGetProp()) { scope.inferQualifiedSlot( node, node.getQualifiedName(), getJSType(node), narrowed); } else { redeclareSimpleVar(scope, node, narrowed); } return scope; } /** * For functions with function parameters, type inference will set the type of * a function literal argument from the function parameter type. */ private void updateTypeOfParameters(Node n, FunctionType fnType) { int i = 0; int childCount = n.getChildCount(); for (Node iParameter : fnType.getParameters()) { if (i + 1 >= childCount) { // TypeCheck#visitParametersList will warn so we bail. return; } JSType iParameterType = getJSType(iParameter); Node iArgument = n.getChildAtIndex(i + 1); JSType iArgumentType = get

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>JSType(iArgument); inferPropertyTypesToMatchConstraint(iArgumentType, iParameterType); if (iParameterType.isFunctionType()) { FunctionType iParameterFnType = iParameterType.toMaybeFunctionType(); if (iArgument.isFunction() && iArgumentType.isFunctionType() && iArgument.getJSDocInfo() == null) { iArgument.setJSType(iParameterFnType); } } i++; } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private void updateTypeOfThisOnClosure(Node n, FunctionType fnType) { // TODO(user): Make the template logic more general. if (fnType.getTemplateTypeName() == null) { return; } int i = 0; int childCount = n.getChildCount(); // Find the parameter whose type is the template type. for (Node iParameter : fnType.getParameters()) { JSType iParameterType = getJSType(iParameter).restrictByNotNullOrUndefined(); if (iParameterType.isTemplateType()) { // Find the actual type of this argument. ObjectType iArgumentType = null; if (i + 1 < childCount) { Node iArgument = n.getChildAtIndex(i + 1); iArgumentType = getJSType(iArgument) .restrictByNotNullOrUndefined() .collapseUnion() .toObjectType(); if (iArgumentType == null) { compiler.report( JSError.make(NodeUtil.getSourceName(iArgument), iArgument, TEMPLATE_TYPE_NOT_OBJECT_TYPE, getJSType(iArgument).toString())); return; } } // Find the parameter whose type is function(this: T, ...) boolean foundTemplateTypeOfThisParameter = false; int j = 0; for (Node jParameter : fnType.getParameters()) { JSType jParameterType = getJSType(jParameter).restrictByNotNullOrUndefined(); if (jParameterType.isFunctionType()) { FunctionType jParameterFnType = jParameterType.toMaybeFunctionType(); if (jParameterFnType.getTypeOfThis().equals(iParameterType)) { foundTemplateTypeOfThisParameter = true; // Find the actual type of the this argument. if (j + 1 >= childCount) { // TypeCheck#visitParameterList will warn so we bail. return; } Node jArgument = n.getChildAtIndex(j + 1); JSType jArgumentType = getJSType(jArgument); if (jArgument.isFunction() && jArgumentType.isFunctionType()) { if (iArgumentType != null && // null and undefined get filtered out above. !iArgumentType.isNoType())

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> { // If it's an function expression, update the type of this // using the actual type of T. FunctionType jArgumentFnType = jArgumentType.toMaybeFunctionType(); if (jArgumentFnType.getTypeOfThis().isUnknownType()) { // The new type will be picked up when we traverse the inner // function. jArgument.setJSType( registry.createFunctionTypeWithNewThisType( jArgumentFnType, iArgumentType)); } } else { // Warn if the anonymous function literal references this. if (NodeUtil.referencesThis( NodeUtil.getFunctionBody(jArgument))) { compiler.report(JSError.make(NodeUtil.getSourceName(n), n, FUNCTION_LITERAL_UNDEFINED_THIS)); } } } // TODO(user): Add code to TypeCheck to check that the // types of the arguments match. } } j++; } if (!foundTemplateTypeOfThisParameter) { compiler.report(JSError.make(NodeUtil.getSourceName(n), n, TEMPLATE_TYPE_OF_THIS_EXPECTED)); return; } } i++; } } private FlowScope traverseNew(Node n, FlowScope scope) { Node constructor = n.getFirstChild(); scope = traverse(constructor, scope); JSType constructorType = constructor.getJSType(); JSType type = null; if (constructorType != null) { constructorType = constructorType.restrictByNotNullOrUndefined(); if (constructorType.isUnknownType()) { type = getNativeType(UNKNOWN_TYPE); } else { FunctionType ct = constructorType.toMaybeFunctionType(); if (ct == null && constructorType instanceof FunctionType) { // If constructorType is a NoObjectType, then toMaybeFunctionType will // return null. But NoObjectType implements the FunctionType // interface, precisely because it can validly construct objects. ct = (FunctionType) constructorType; } if (ct != null && ct.isConstructor()) { type = ct.getInstanceType(); } } } n.setJSType(type); for (Node arg = constructor.getNext(); arg != null; arg = arg.getNext()) { scope = traverse(arg, scope); } return scope; } private BooleanOutcomePair traverseAnd(Node n, FlowScope scope) { return traverseShortCircuitingBinOp(n, scope, true); } private FlowScope traverseChildren(Node n, FlowScope scope) { for (Node el = n.getFirstChild(); el != null; el = el.getNext()) { scope = traverse(el, scope); } return scope; } private FlowScope traverseGetElem(Node n, FlowScope scope) { scope = traverseChildren(n, scope); ObjectType objType = ObjectType.cast( getJSType

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>(n.getFirstChild()).restrictByNotNullOrUndefined()); if (objType != null) { JSType type = objType.getParameterType(); if (type != null) { n.setJSType(type); } } return dereferencePointer(n.getFirstChild(), scope); } private FlowScope traverseGetProp(Node n, FlowScope scope) { Node objNode = n.getFirstChild(); Node property = n.getLastChild(); scope = traverseChildren(n, scope); n.setJSType( getPropertyType( objNode.getJSType(), property.getString(), n, scope)); return dereferencePointer(n.getFirstChild(), scope); } /** * Suppose X is an object with inferred properties. * Suppose also that X is used in a way where it would only type-check * correctly if some of those properties are widened. * Then we should be polite and automatically widen X's properties for him. * * For a concrete example, consider: * param x {{prop: (number|undefined)}} * function f(x) {} * f({}); * * If we give the anonymous object an inferred property of (number|undefined), * then this code will type-check appropriately. */ private void inferPropertyTypesToMatchConstraint( JSType type, JSType constraint) { ObjectType constraintObj = ObjectType.cast(constraint.restrictByNotNullOrUndefined()); if (constraintObj != null && constraintObj.isRecordType()) { ObjectType objType = ObjectType.cast(type.restrictByNotNullOrUndefined()); if (objType != null) { for (String prop : constraintObj.getOwnPropertyNames()) { JSType propType = constraintObj.getPropertyType(prop); if (!objType.isPropertyTypeDeclared(prop)) { JSType typeToInfer = propType; if (!objType.hasProperty(prop)) { typeToInfer = getNativeType(VOID_TYPE).getLeastSupertype(propType); } objType.defineInferredProperty(prop, typeToInfer, null); } } } } } /** * If we access a property of a symbol, then that symbol is not * null or undefined. */ private FlowScope dereferencePointer(Node n, FlowScope scope) { if (n.isQualifiedName()) { JSType type = getJSType(n); JSType narrowed = type.restrictByNotNullOrUndefined(); if (type != narrowed) { scope = narrowScope(scope, n, narrowed); } } return scope; } private JSType getPropertyType(JSType objType, String propName, Node n, FlowScope scope) { // Scopes sometimes contain inferred type info about qualified names. String qualifiedName = n.getQualifiedName(); StaticSlot<JSType>

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> var = scope.getSlot(qualifiedName); if (var != null) { JSType varType = var.getType(); if (varType != null) { if (varType.equals(getNativeType(UNKNOWN_TYPE)) && var != syntacticScope.getSlot(qualifiedName)) { // If the type of this qualified name has been checked in this scope, // then use CHECKED_UNKNOWN_TYPE instead to indicate that. return getNativeType(CHECKED_UNKNOWN_TYPE); } else { return varType; } } } JSType propertyType = null; if (objType != null) { propertyType = objType.findPropertyType(propName); } if ((propertyType == null || propertyType.isUnknownType()) && qualifiedName != null) { // If we find this node in the registry, then we can infer its type. ObjectType regType = ObjectType.cast(registry.getType(qualifiedName)); if (regType != null) { propertyType = regType.getConstructor(); } } return propertyType; } private BooleanOutcomePair traverseOr(Node n, FlowScope scope) { return traverseShortCircuitingBinOp(n, scope, false); } private BooleanOutcomePair traverseShortCircuitingBinOp( Node n, FlowScope scope, boolean condition) { Node left = n.getFirstChild(); Node right = n.getLastChild(); // type the left node BooleanOutcomePair leftLiterals = traverseWithinShortCircuitingBinOp(left, scope.createChildFlowScope()); JSType leftType = left.getJSType(); // reverse abstract interpret the left node to produce the correct // scope in which to verify the right node FlowScope rightScope = reverseInterpreter. getPreciserScopeKnowingConditionOutcome( left, leftLiterals.getOutcomeFlowScope(left.getType(), condition), condition); // type the right node BooleanOutcomePair rightLiterals = traverseWithinShortCircuitingBinOp( right, rightScope.createChildFlowScope()); JSType rightType = right.getJSType(); JSType type; BooleanOutcomePair literals; if (leftType != null && rightType != null) { leftType = leftType.getRestrictedTypeGivenToBooleanOutcome(!condition); if (leftLiterals.toBooleanOutcomes == BooleanLiteralSet.get(!condition)) { // Use the restricted left type, since the right side never gets // evaluated. type = leftType; literals = leftLiterals; } else { // Use the join of the restricted left type knowing the outcome of the // ToBoolean predicate and of the right type. type = leftType.getLeastSupertype(rightType); literals = getBooleanOutcomePair(leftLiterals, rightLiterals, condition); } // Exclude the boolean type if

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> the literal set is empty because a boolean // can never actually be returned. if (literals.booleanValues == BooleanLiteralSet.EMPTY && getNativeType(BOOLEAN_TYPE).isSubtype(type)) { // Exclusion only make sense for a union type. if (type.isUnionType()) { type = type.toMaybeUnionType().getRestrictedUnion( getNativeType(BOOLEAN_TYPE)); } } } else { type = null; literals = new BooleanOutcomePair( BooleanLiteralSet.BOTH, BooleanLiteralSet.BOTH, leftLiterals.getJoinedFlowScope(), rightLiterals.getJoinedFlowScope()); } n.setJSType(type); return literals; } private BooleanOutcomePair traverseWithinShortCircuitingBinOp(Node n, FlowScope scope) { switch (n.getType()) { case Token.AND: return traverseAnd(n, scope); case Token.OR: return traverseOr(n, scope); default: scope = traverse(n, scope); return newBooleanOutcomePair(n.getJSType(), scope); } } /** * Infers the boolean outcome pair that can be taken by a * short-circuiting binary operation ({@code &&} or {@code ||}). * @see #getBooleanOutcomes(BooleanLiteralSet, BooleanLiteralSet, boolean) */ BooleanOutcomePair getBooleanOutcomePair(BooleanOutcomePair left, BooleanOutcomePair right, boolean condition) { return new BooleanOutcomePair( getBooleanOutcomes(left.toBooleanOutcomes, right.toBooleanOutcomes, condition), getBooleanOutcomes(left.booleanValues, right.booleanValues, condition), left.getJoinedFlowScope(), right.getJoinedFlowScope()); } /** * Infers the boolean literal set that can be taken by a * short-circuiting binary operation ({@code &&} or {@code ||}). * @param left the set of possible {@code ToBoolean} predicate results for * the expression on the left side of the operator * @param right the set of possible {@code ToBoolean} predicate results for * the expression on the right side of the operator * @param condition the left side {@code ToBoolean} predicate result that * causes the right side to get evaluated (i.e. not short-circuited) * @return a set of possible {@code ToBoolean} predicate results for the * entire expression */ static BooleanLiteralSet getBooleanOutcomes(BooleanLiteralSet left, BooleanLiteralSet right, boolean condition) { return right.union(left.intersection(BooleanLiteralSet.get(!condition))); } /** * When traversing short-circuiting binary operations, we need to keep track * of two sets of boolean literals: * 1. {@code toBooleanOutcomes}: boolean literals as converted from any types

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>.getScope() == syntacticScope; } /** * This method gets the JSType from the Node argument and verifies that it is * present. */ private JSType getJSType(Node n) { JSType jsType = n.getJSType(); if (jsType == null) { // TODO(nicksantos): This branch indicates a compiler bug, not worthy of // halting the compilation but we should log this and analyze to track // down why it happens. This is not critical and will be resolved over // time as the type checker is extended. return getNativeType(UNKNOWN_TYPE); } else { return jsType; } } private JSType getNativeType(JSTypeNative typeId) { return registry.getNativeType(typeId); } }

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>; /** * Callback */ public interface Callback { /** * <p>Visits a node in pre order (before visiting its children) and decides * whether this node's children should be traversed. If children are * traversed, they will be visited by * {@link #visit(NodeTraversal, Node, Node)} in post order.</p> * <p>Implementations can have side effects (e.g. modifying the parse * tree).</p> * @return whether the children of this node should be visited */ boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent); /** * <p>Visits a node in post order (after its children have been visited). * A node is visited only if all its parents should be traversed * ({@link #shouldTraverse(NodeTraversal, Node, Node)}).</p> * <p>Implementations can have side effects (e.g. modifying the parse * tree).</p> */ void visit(NodeTraversal t, Node n, Node parent); } /** * Callback that also knows about scope changes */ public interface ScopedCallback extends Callback { /** * Called immediately after entering a new scope. The new scope can * be accessed through t.getScope() */ void enterScope(NodeTraversal t); /** * Called immediately before exiting a scope. The ending scope can * be accessed through t.getScope() */ void exitScope(NodeTraversal t); } /** * Abstract callback to visit all nodes in post order. */ public abstract static class AbstractPostOrderCallback implements Callback { @Override public final boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { return true; } } /** * Abstract scoped callback to visit all nodes in post order. */ public abstract static class AbstractScopedCallback implements ScopedCallback { @Override public final boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { return true; } @Override public void enterScope(NodeTraversal t) {} @Override public void exitScope(NodeTraversal t) {} } /** * Abstract callback to visit all nodes but not traverse into function * bodies. */ public abstract static class AbstractShallowCallback implements Callback { @Override public final boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { // We do want to traverse the name of a named function, but we don't // want to traverse the arguments or body. return parent == null || parent.getType() != Token.FUNCTION || n == parent.getFirstChild(); } } /** * Abstract callback to visit all structure and statement nodes but doesn't * traverse into functions or expressions. */ public abstract static class AbstractShallowStatementCallback implements Callback { @Override public

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> final boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { return parent == null || NodeUtil.isControlStructure(parent) || NodeUtil.isStatementBlock(parent); } } /** * Abstract callback to visit a pruned set of nodes. */ public abstract static class AbstractNodeTypePruningCallback implements Callback { private final Set<Integer> nodeTypes; private final boolean include; /** * Creates an abstract pruned callback. * @param nodeTypes the nodes to include in the traversal */ public AbstractNodeTypePruningCallback(Set<Integer> nodeTypes) { this(nodeTypes, true); } /** * Creates an abstract pruned callback. * @param nodeTypes the nodes to include/exclude in the traversal * @param include whether to include or exclude the nodes in the traversal */ public AbstractNodeTypePruningCallback(Set<Integer> nodeTypes, boolean include) { this.nodeTypes = nodeTypes; this.include = include; } @Override public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { return include == nodeTypes.contains(n.getType()); } } /** * Creates a node traversal using the specified callback interface. */ public NodeTraversal(AbstractCompiler compiler, Callback cb) { this(compiler, cb, new SyntacticScopeCreator(compiler)); } /** * Creates a node traversal using the specified callback interface * and the scope creator. */ public NodeTraversal(AbstractCompiler compiler, Callback cb, ScopeCreator scopeCreator) { this.callback = cb; if (cb instanceof ScopedCallback) { this.scopeCallback = (ScopedCallback) cb; } this.compiler = compiler; this.inputId = null; this.sourceName = ""; this.scopeCreator = scopeCreator; } private void throwUnexpectedException(Exception unexpectedException) { // If there's an unexpected exception, try to get the // line number of the code that caused it. String message = unexpectedException.getMessage(); // TODO(user): It is possible to get more information if curNode or // its parent is missing. We still have the scope stack in which it is still // very useful to find out at least which function caused the exception. if (inputId != null) { message = unexpectedException.getMessage() + "\n" + formatNodeContext("Node", curNode) + (curNode == null ? "" : formatNodeContext("Parent", curNode.getParent())); } compiler.throwInternalError(message, unexpectedException); } private String formatNodeContext(String label, Node n) { if (n == null) { return " " + label + ": NULL"; } return " " + label + "(" + n.toString(false, false,

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> false) + "): " + formatNodePosition(n); } /** * Traverses a parse tree recursively. */ public void traverse(Node root) { try { inputId = NodeUtil.getInputId(root); sourceName = ""; curNode = root; pushScope(root); traverseBranch(root, null); popScope(); } catch (Exception unexpectedException) { throwUnexpectedException(unexpectedException); } } public void traverseRoots(Node ... roots) { traverseRoots(Lists.newArrayList(roots)); } public void traverseRoots(List<Node> roots) { if (roots.isEmpty()) { return; } try { Node scopeRoot = roots.get(0).getParent(); Preconditions.checkState(scopeRoot != null); inputId = NodeUtil.getInputId(scopeRoot); sourceName = ""; curNode = scopeRoot; pushScope(scopeRoot); for (Node root : roots) { Preconditions.checkState(root.getParent() == scopeRoot); traverseBranch(root, scopeRoot); } popScope(); } catch (Exception unexpectedException) { throwUnexpectedException(unexpectedException); } } private static final String MISSING_SOURCE = "[source unknown]"; private String formatNodePosition(Node n) { if (n == null) { return MISSING_SOURCE + "\n"; } int lineNumber = n.getLineno(); int columnNumber = n.getCharno(); String src = compiler.getSourceLine(sourceName, lineNumber); if (src == null) { src = MISSING_SOURCE; } return sourceName + ":" + lineNumber + ":" + columnNumber + "\n" + src + "\n"; } /** * Traverses a parse tree recursively with a scope, starting with the given * root. This should only be used in the global scope. Otherwise, use * {@link #traverseAtScope}. */ void traverseWithScope(Node root, Scope s) { Preconditions.checkState(s.isGlobal()); inputId = null; sourceName = ""; curNode = root; pushScope(s); traverseBranch(root, null); popScope(); } /** * Traverses a parse tree recursively with a scope, starting at that scope's * root. */ void traverseAtScope(Scope s) { Node n = s.getRootNode(); if (n.isFunction()) { // We need to do some extra magic to make sure that the scope doesn't // get re-created when we dive into the function. if (inputId == null) { inputId = NodeUtil.getInputId(n); } sourceName = getSourceName(n); curNode = n; pushScope(s); Node args = n.getFirstChild().getNext

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>(); Node body = args.getNext(); traverseBranch(args, n); traverseBranch(body, n); popScope(); } else { traverseWithScope(n, s); } } /** * Traverses an inner node recursively with a refined scope. An inner node may * be any node with a non {@code null} parent (i.e. all nodes except the * root). * * @param node the node to traverse * @param parent the node's parent, it may be not be {@code null} * @param refinedScope the refined scope of the scope currently at the top of * the scope stack or in trivial cases that very scope or {@code null} */ protected void traverseInnerNode(Node node, Node parent, Scope refinedScope) { Preconditions.checkNotNull(parent); if (refinedScope != null && getScope() != refinedScope) { curNode = node; pushScope(refinedScope); traverseBranch(node, parent); popScope(); } else { traverseBranch(node, parent); } } /** * Gets the compiler. */ public Compiler getCompiler() { // TODO(nicksantos): Remove this type cast. This is just temporary // while refactoring. return (Compiler) compiler; } /** * Gets the current line number, or zero if it cannot be determined. The line * number is retrieved lazily as a running time optimization. */ public int getLineNumber() { Node cur = curNode; while (cur != null) { int line = cur.getLineno(); if (line >=0) { return line; } cur = cur.getParent(); } return 0; } /** * Gets the current input source name. * * @return A string that may be empty, but not null */ public String getSourceName() { return sourceName; } /** * Gets the current input source. */ public CompilerInput getInput() { return compiler.getInput(inputId); } /** * Gets the current input module. */ public JSModule getModule() { CompilerInput input = getInput(); return input == null ? null : input.getModule(); } /** Returns the node currently being traversed. */ public Node getCurrentNode() { return curNode; } /** * Traverses a node recursively. */ public static void traverse( AbstractCompiler compiler, Node root, Callback cb) { NodeTraversal t = new NodeTraversal(compiler, cb); t.traverse(root); } /** * Traverses a list of node trees. */ public static void traverseRoots( AbstractCompiler compiler, List<Node> roots, Callback cb) { NodeTraversal t = new NodeTraversal(compiler, cb); t

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>.traverseRoots(roots); } /** * Traverses a branch. */ @SuppressWarnings("fallthrough") private void traverseBranch(Node n, Node parent) { int type = n.getType(); if (type == Token.SCRIPT) { inputId = n.getInputId(); sourceName = getSourceName(n); } curNode = n; if (!callback.shouldTraverse(this, n, parent)) return; switch (type) { case Token.FUNCTION: traverseFunction(n, parent); break; default: for (Node child = n.getFirstChild(); child != null; ) { // child could be replaced, in which case our child node // would no longer point to the true next Node next = child.getNext(); traverseBranch(child, n); child = next; } break; } curNode = n; callback.visit(this, n, parent); } /** * Traverses a function. */ private void traverseFunction(Node n, Node parent) { Preconditions.checkState(n.getChildCount() == 3); Preconditions.checkState(n.isFunction()); final Node fnName = n.getFirstChild(); boolean isFunctionExpression = (parent != null) && NodeUtil.isFunctionExpression(n); if (!isFunctionExpression) { // Functions declarations are in the scope containing the declaration. traverseBranch(fnName, n); } curNode = n; pushScope(n); if (isFunctionExpression) { // Function expression names are only accessible within the function // scope. traverseBranch(fnName, n); } final Node args = fnName.getNext(); final Node body = args.getNext(); // Args traverseBranch(args, n); // Body Preconditions.checkState(body.getNext() == null && body.isBlock()); traverseBranch(body, n); popScope(); } /** Examines the functions stack for the last instance of a function node. */ @SuppressWarnings("unchecked") public Node getEnclosingFunction() { if (scopes.size() + scopeRoots.size() < 2) { return null; } else { if (scopeRoots.isEmpty()) { return scopes.peek().getRootNode(); } else { return scopeRoots.peek(); } } } /** Creates a new scope (e.g. when entering a function). */ private void pushScope(Node node) { Preconditions.checkState(curNode != null); scopeRoots.push(node); cfgs.push(null); if (scopeCallback != null) { scopeCallback.enterScope(this); } } /** Creates a new scope (e.g. when entering a function). */ private void pushScope(Scope s) { Preconditions.checkState(curNode

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> != null); scopes.push(s); cfgs.push(null); if (scopeCallback != null) { scopeCallback.enterScope(this); } } /** Pops back to the previous scope (e.g. when leaving a function). */ private void popScope() { if (scopeCallback != null) { scopeCallback.exitScope(this); } if (scopeRoots.isEmpty()) { scopes.pop(); } else { scopeRoots.pop(); } cfgs.pop(); } /** Gets the current scope. */ public Scope getScope() { Scope scope = scopes.isEmpty() ? null : scopes.peek(); if (scopeRoots.isEmpty()) { return scope; } Iterator<Node> it = scopeRoots.descendingIterator(); while (it.hasNext()) { scope = scopeCreator.createScope(it.next(), scope); scopes.push(scope); } scopeRoots.clear(); return scope; } /** Gets the control flow graph for the current JS scope. */ public ControlFlowGraph<Node> getControlFlowGraph() { if (cfgs.peek() == null) { ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); cfa.process(null, getScopeRoot()); cfgs.pop(); cfgs.push(cfa.getCfg()); } return cfgs.peek(); } /** Returns the current scope's root. */ public Node getScopeRoot() { if (scopeRoots.isEmpty()) { return scopes.peek().getRootNode(); } else { return scopeRoots.peek(); } } /** * Determines whether the traversal is currently in the global scope. */ boolean inGlobalScope() { return getScopeDepth() <= 1; } int getScopeDepth() { return scopes.size() + scopeRoots.size(); } public boolean hasScope() { return !(scopes.isEmpty() && scopeRoots.isEmpty()); } /** Reports a diagnostic (error or warning) */ public void report(Node n, DiagnosticType diagnosticType, String... arguments) { JSError error = JSError.make(getSourceName(), n, diagnosticType, arguments); compiler.report(error); } private static String getSourceName(Node n) { String name = n.getSourceFileName(); return name == null ? "" : name; } InputId getInputId() { return inputId; } /** * Creates a JSError during NodeTraversal. * * @param n Determines the line and char position within the source file name * @param type The DiagnosticType * @param arguments Arguments to be incorporated into the message */ public JSError makeError(Node n, CheckLevel level, DiagnosticType type, String... arguments) { return JSError.make

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>(getSourceName(), n, level, type, arguments); } /** * Creates a JSError during NodeTraversal. * * @param n Determines the line and char position within the source file name * @param type The DiagnosticType * @param arguments Arguments to be incorporated into the message */ public JSError makeError(Node n, DiagnosticType type, String... arguments) { return JSError.make(getSourceName(), n, type, arguments); } }

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>/* * Copyright 2008 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp.graph; import java.util.List; /** * A generic directed graph. * * * @param <N> Value type that the graph node stores. * @param <E> Value type that the graph edge stores. */ public abstract class DiGraph<N, E> extends Graph<N, E> { /** * Gets an immutable iterable over all the nodes in the graph. */ public abstract Iterable<DiGraphNode<N, E>> getDirectedGraphNodes(); /** * Gets an immutable list of out edges of the given node. */ public abstract List<DiGraphEdge<N, E>> getOutEdges(N nodeValue); /** * Gets an immutable list of in edges of the given node. */ public abstract List<DiGraphEdge<N, E>> getInEdges(N nodeValue); public abstract List<DiGraphNode<N, E>> getDirectedPredNodes( DiGraphNode<N, E> n); public abstract List<DiGraphNode<N, E>> getDirectedSuccNodes( DiGraphNode<N, E> n); public abstract List<DiGraphNode<N, E>> getDirectedPredNodes(N nodeValue); public abstract List<DiGraphNode<N, E>> getDirectedSuccNodes(N nodeValue); public abstract DiGraphNode<N, E> createDirectedGraphNode(N nodeValue); public abstract DiGraphNode<N, E> getDirectedGraphNode(N nodeValue); public abstract List<DiGraphEdge<N, E>> getDirectedGraphEdges(N n1, N n2); /** * Disconnects all edges from n1 to n2. * * @param n1 Source node. * @param n2 Destination node. */ public abstract void disconnectInDirection(N n1, N n2); /** * Checks whether two nodes in the graph are connected via a directed edge. * * @param n1 Node 1. * @param n2 Node 2. * @return <code>true</code> if the graph contains edge from n1 to

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> new TypeInferringCallback(), scopeCreator); inferTypes.traverseWithScope(node, topScope); } void inferTypes(NodeTraversal t, Node n, Scope scope) { TypeInference typeInference = new TypeInference( compiler, computeCfg(n), reverseInterpreter, scope, assertionFunctionsMap); try { typeInference.analyze(); // Resolve any new type names found during the inference. compiler.getTypeRegistry().resolveTypesInScope(scope); } catch (DataFlowAnalysis.MaxIterationsExceededException e) { compiler.report(t.makeError(n, DATAFLOW_ERROR)); } } private class TypeInferringCallback implements ScopedCallback { @Override public void enterScope(NodeTraversal t) { Scope scope = t.getScope(); Node node = t.getCurrentNode(); if (scope.isGlobal()) { inferTypes(t, node, scope); } } @Override public void exitScope(NodeTraversal t) { Scope scope = t.getScope(); Node node = t.getCurrentNode(); if (scope.isLocal()) { inferTypes(t, node, scope); } } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { // Do nothing } } private ControlFlowGraph<Node> computeCfg(Node n) { ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, false); cfa.process(null, n); return cfa.getCfg(); } }

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> "name {0} is not undefined in the externs."); static final DiagnosticType INVALID_FUNCTION_DECL = DiagnosticType.error("JSC_INVALID_FUNCTION_DECL", "Syntax error: function declaration must have a name"); private CompilerInput synthesizedExternsInput = null; private Node synthesizedExternsRoot = null; // Vars that still need to be declared in externs. These will be declared // at the end of the pass, or when we see the equivalent var declared // in the normal code. private final Set<String> varsToDeclareInExterns = Sets.newHashSet(); private final AbstractCompiler compiler; // Whether this is the post-processing sanity check. private final boolean sanityCheck; // Whether extern checks emit error. private final boolean strictExternCheck; VarCheck(AbstractCompiler compiler) { this(compiler, false); } VarCheck(AbstractCompiler compiler, boolean sanityCheck) { this.compiler = compiler; this.strictExternCheck = compiler.getErrorLevel( JSError.make("", 0, 0, UNDEFINED_EXTERN_VAR_ERROR)) == CheckLevel.ERROR; this.sanityCheck = sanityCheck; } @Override public void process(Node externs, Node root) { // Don't run externs-checking in sanity check mode. Normalization will // remove duplicate VAR declarations, which will make // externs look like they have assigns. if (!sanityCheck) { NodeTraversal.traverse(compiler, externs, new NameRefInExternsCheck()); } NodeTraversal.traverseRoots( compiler, Lists.newArrayList(externs, root), this); for (String varName : varsToDeclareInExterns) { createSynthesizedExternVar(varName); } } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { Preconditions.checkState(scriptRoot.isScript()); NodeTraversal t = new NodeTraversal(compiler, this); // Note we use the global scope to prevent wrong "undefined-var errors" on // variables that are defined in other js files. t.traverseWithScope(scriptRoot, SyntacticScopeCreator.generateUntypedTopScope(compiler)); // TODO(bashir) Check if we need to createSynthesizedExternVar like process. } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() != Token.NAME) { return; } String varName = n.getString(); // Only a function can have an empty name. if (varName.isEmpty()) { Preconditions.checkState(parent.isFunction()); // A function declaration with an empty name passes Rhino, // but is supposed to be a syntax error according to the spec. if (!NodeUtil.isFunctionExpression(parent)) {

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> t.report(n, INVALID_FUNCTION_DECL); } return; } // Check if this is a declaration for a var that has been declared // elsewhere. If so, mark it as a duplicate. if ((parent.isVar() || NodeUtil.isFunctionDeclaration(parent)) && varsToDeclareInExterns.contains(varName)) { createSynthesizedExternVar(varName); n.addSuppression("duplicate"); } // Check that the var has been declared. Scope scope = t.getScope(); Scope.Var var = scope.getVar(varName); if (var == null) { if (NodeUtil.isFunctionExpression(parent)) { // e.g. [ function foo() {} ], it's okay if "foo" isn't defined in the // current scope. } else { // The extern checks are stricter, don't report a second error. if (!strictExternCheck || !t.getInput().isExtern()) { t.report(n, UNDEFINED_VAR_ERROR, varName); } if (sanityCheck) { throw new IllegalStateException("Unexpected variable " + varName); } else { createSynthesizedExternVar(varName); scope.getGlobalScope().declare(varName, n, null, getSynthesizedExternsInput()); } } return; } CompilerInput currInput = t.getInput(); CompilerInput varInput = var.input; if (currInput == varInput || currInput == null || varInput == null) { // The variable was defined in the same file. This is fine. return; } // Check module dependencies. JSModule currModule = currInput.getModule(); JSModule varModule = varInput.getModule(); JSModuleGraph moduleGraph = compiler.getModuleGraph(); if (varModule != currModule && varModule != null && currModule != null) { if (moduleGraph.dependsOn(currModule, varModule)) { // The module dependency was properly declared. } else { if (!sanityCheck && scope.isGlobal()) { if (moduleGraph.dependsOn(varModule, currModule)) { // The variable reference violates a declared module dependency. t.report(n, VIOLATED_MODULE_DEP_ERROR, currModule.getName(), varModule.getName(), varName); } else { // The variable reference is between two modules that have no // dependency relationship. This should probably be considered an // error, but just issue a warning for now. t.report(n, MISSING_MODULE_DEP_ERROR, currModule.getName(), varModule.getName(), varName); } } else { t.report(n, STRICT_MODULE_DEP_ERROR, currModule.getName(), varModule.getName(), varName); } } } } /** * Create

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> a new variable in a synthetic script. This will prevent * subsequent compiler passes from crashing. */ private void createSynthesizedExternVar(String varName) { Node nameNode = Node.newString(Token.NAME, varName); // Mark the variable as constant if it matches the coding convention // for constant vars. // NOTE(nicksantos): honestly, i'm not sure how much this matters. // AFAIK, all people who use the CONST coding convention also // compile with undeclaredVars as errors. We have some test // cases for this configuration though, and it makes them happier. if (compiler.getCodingConvention().isConstant(varName)) { nameNode.putBooleanProp(Node.IS_CONSTANT_NAME, true); } getSynthesizedExternsRoot().addChildToBack( new Node(Token.VAR, nameNode)); varsToDeclareInExterns.remove(varName); compiler.reportCodeChange(); } /** * A check for name references in the externs inputs. These used to prevent * a variable from getting renamed, but no longer have any effect. */ private class NameRefInExternsCheck extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isName()) { switch (parent.getType()) { case Token.VAR: case Token.FUNCTION: case Token.PARAM_LIST: // These are okay. break; case Token.GETPROP: if (n == parent.getFirstChild()) { Scope scope = t.getScope(); Scope.Var var = scope.getVar(n.getString()); if (var == null) { t.report(n, UNDEFINED_EXTERN_VAR_ERROR, n.getString()); varsToDeclareInExterns.add(n.getString()); } } break; default: t.report(n, NAME_REFERENCE_IN_EXTERNS_ERROR, n.getString()); Scope scope = t.getScope(); Scope.Var var = scope.getVar(n.getString()); if (var == null) { varsToDeclareInExterns.add(n.getString()); } break; } } } } /** Lazily create a "new" externs input for undeclared variables. */ private CompilerInput getSynthesizedExternsInput() { if (synthesizedExternsInput == null) { synthesizedExternsInput = compiler.newExternInput(SYNTHETIC_VARS_DECLAR); } return synthesizedExternsInput; } /** Lazily create a "new" externs root for undeclared variables. */ private Node getSynthesizedExternsRoot() { if (synthesizedExternsRoot == null) {

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> current traversal is in. */ private final Deque<BasicBlock> blockStack = new ArrayDeque<BasicBlock>(); /** * Source of behavior at various points in the traversal. */ private final Behavior behavior; /** * Javascript compiler to use in traversing. */ private final AbstractCompiler compiler; /** * Only collect references for filtered variables. */ private final Predicate<Var> varFilter; /** * Constructor initializes block stack. */ ReferenceCollectingCallback(AbstractCompiler compiler, Behavior behavior) { this(compiler, behavior, Predicates.<Var>alwaysTrue()); } /** * Constructor only collects references that match the given variable. * * The test for Var equality uses reference equality, so it's necessary to * inject a scope when you traverse. */ ReferenceCollectingCallback(AbstractCompiler compiler, Behavior behavior, Predicate<Var> varFilter) { this.compiler = compiler; this.behavior = behavior; this.varFilter = varFilter; } /** * Convenience method for running this pass over a tree with this * class as a callback. */ @Override public void process(Node externs, Node root) { NodeTraversal.traverseRoots( compiler, Lists.newArrayList(externs, root), this); } /** * Same as process but only runs on a part of AST associated to one script. */ @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { NodeTraversal.traverse(compiler, scriptRoot, this); } /** * Gets the variables that were referenced in this callback. */ @Override public Iterable<Var> getAllSymbols() { return referenceMap.keySet(); } @Override public Scope getScope(Var var) { return var.scope; } /** * Gets the reference collection for the given variable. */ @Override public ReferenceCollection getReferences(Var v) { return referenceMap.get(v); } /** * For each node, update the block stack and reference collection * as appropriate. */ @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isName()) { Var v; if (n.getString().equals("arguments")) { v = t.getScope().getArgumentsVar(); } else { v = t.getScope().getVar(n.getString()); } if (v != null && varFilter.apply(v)) { addReference(t, v, new Reference(n, t, blockStack.peek())); } } if (isBlockBoundary(n, parent)) { blockStack.pop(); } } /** * Updates block stack and invokes any additional behavior. */ @Override public void enterScope(NodeTraversal t) { Node n = t.getScope().getRootNode();

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> BasicBlock parent = blockStack.isEmpty() ? null : blockStack.peek(); blockStack.push(new BasicBlock(parent, n)); } /** * Updates block statck and invokes any additional behavior. */ @Override public void exitScope(NodeTraversal t) { blockStack.pop(); if (t.getScope().isGlobal()) { // Update global scope reference lists when we are done with it. compiler.updateGlobalVarReferences(referenceMap, t.getScopeRoot()); behavior.afterExitScope(t, compiler.getGlobalVarReferences()); } else { behavior.afterExitScope(t, new ReferenceMapWrapper(referenceMap)); } } /** * Updates block stack. */ @Override public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { // If node is a new basic block, put on basic block stack if (isBlockBoundary(n, parent)) { blockStack.push(new BasicBlock(blockStack.peek(), n)); } return true; } /** * @return true if this node marks the start of a new basic block */ private static boolean isBlockBoundary(Node n, Node parent) { if (parent != null) { switch (parent.getType()) { case Token.DO: case Token.FOR: case Token.TRY: case Token.WHILE: case Token.WITH: // NOTE: TRY has up to 3 child blocks: // TRY // BLOCK // BLOCK // CATCH // BLOCK // Note that there is an explcit CATCH token but no explicit // FINALLY token. For simplicity, we consider each BLOCK // a separate basic BLOCK. return true; case Token.AND: case Token.HOOK: case Token.IF: case Token.OR: // The first child of a conditional is not a boundary, // but all the rest of the children are. return n != parent.getFirstChild(); } } return n.isCase(); } private void addReference(NodeTraversal t, Var v, Reference reference) { // Create collection if none already ReferenceCollection referenceInfo = referenceMap.get(v); if (referenceInfo == null) { referenceInfo = new ReferenceCollection(); referenceMap.put(v, referenceInfo); } // Add this particular reference referenceInfo.add(reference, t, v); } interface ReferenceMap { ReferenceCollection getReferences(Var var); } private static class ReferenceMapWrapper implements ReferenceMap { private final Map<Var, ReferenceCollection> referenceMap; public ReferenceMapWrapper(Map<Var, ReferenceCollection> referenceMap) { this.referenceMap = referenceMap; } @Override public ReferenceCollection getReferences(Var var) { return referenceMap.get(var); }

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>parent = parent.getParent(); return DECLARATION_PARENTS.contains(parent.getType()) || parent.isParamList() && grandparent.isFunction(); } boolean isVarDeclaration() { return getParent().isVar(); } boolean isHoistedFunction() { return NodeUtil.isHoistedFunctionDeclaration(getParent()); } /** * Determines whether the variable is initialized at the declaration. */ boolean isInitializingDeclaration() { // VAR is the only type of variable declaration that may not initialize // its variable. Catch blocks, named functions, and parameters all do. return isDeclaration() && getParent().getType() != Token.VAR || nameNode.getFirstChild() != null; } /** * @return For an assignment, variable declaration, or function declaration * return the assigned value, otherwise null. */ Node getAssignedValue() { Node parent = getParent(); return (parent.isFunction()) ? parent : NodeUtil.getAssignedValue(nameNode); } BasicBlock getBasicBlock() { return basicBlock; } Node getParent() { return getNode().getParent(); } Node getGrandparent() { Node parent = getParent(); return parent == null ? null : parent.getParent(); } private static boolean isLhsOfForInExpression(Node n) { Node parent = n.getParent(); if (parent.isVar()) { return isLhsOfForInExpression(parent); } return NodeUtil.isForIn(parent) && parent.getFirstChild() == n; } boolean isSimpleAssignmentToName() { Node parent = getParent(); return parent.isAssign() && parent.getFirstChild() == nameNode; } boolean isLvalue() { Node parent = getParent(); int parentType = parent.getType(); return (parentType == Token.VAR && nameNode.getFirstChild() != null) || parentType == Token.INC || parentType == Token.DEC || (NodeUtil.isAssignmentOp(parent) && parent.getFirstChild() == nameNode) || isLhsOfForInExpression(nameNode); } Scope getScope() { return scope; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ private final boolean isFunction; /** * Whether this block denotes a loop. */ private final boolean isLoop; /** * Creates a new block. * @param parent The containing block. * @param root The root

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> } check(jsRoot, false); } /** Main entry point of this phase for testing code. */ public Scope processForTesting(Node externsRoot, Node jsRoot) { Preconditions.checkState(scopeCreator == null); Preconditions.checkState(topScope == null); Preconditions.checkState(jsRoot.getParent() != null); Node externsAndJsRoot = jsRoot.getParent(); scopeCreator = new MemoizedScopeCreator(new TypedScopeCreator(compiler)); topScope = scopeCreator.createScope(externsAndJsRoot, null); TypeInferencePass inference = new TypeInferencePass(compiler, reverseInterpreter, topScope, scopeCreator); inference.process(externsRoot, jsRoot); process(externsRoot, jsRoot); return topScope; } public void check(Node node, boolean externs) { Preconditions.checkNotNull(node); NodeTraversal t = new NodeTraversal(compiler, this, scopeCreator); inExterns = externs; t.traverseWithScope(node, topScope); if (externs) { inferJSDocInfo.process(node, null); } else { inferJSDocInfo.process(null, node); } } private void checkNoTypeCheckSection(Node n, boolean enterSection) { switch (n.getType()) { case Token.SCRIPT: case Token.BLOCK: case Token.VAR: case Token.FUNCTION: case Token.ASSIGN: JSDocInfo info = n.getJSDocInfo(); if (info != null && info.isNoTypeCheck()) { if (enterSection) { noTypeCheckSection++; } else { noTypeCheckSection--; } } validator.setShouldReport(noTypeCheckSection == 0); break; } } private void report(NodeTraversal t, Node n, DiagnosticType diagnosticType, String... arguments) { if (noTypeCheckSection == 0) { t.report(n, diagnosticType, arguments); } } @Override public boolean shouldTraverse( NodeTraversal t, Node n, Node parent) { checkNoTypeCheckSection(n, true); switch (n.getType()) { case Token.FUNCTION: // normal type checking final Scope outerScope = t.getScope(); final String functionPrivateName = n.getFirstChild().getString(); if (functionPrivateName != null && functionPrivateName.length() > 0 && outerScope.isDeclared(functionPrivateName, false) && // Ideally, we would want to check whether the type in the scope // differs from the type being defined, but then the extern // redeclarations of built-in types generates spurious warnings. !(outerScope.getVar( functionPrivateName).getType() instanceof FunctionType)) { report(t, n, FUNCTION_

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>MASKS_VARIABLE, functionPrivateName); } // TODO(user): Only traverse the function's body. The function's // name and arguments are traversed by the scope creator, and ideally // should not be traversed by the type checker. break; } return true; } /** * This is the meat of the type checking. It is basically one big switch, * with each case representing one type of parse tree node. The individual * cases are usually pretty straightforward. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. * @param parent The parent of the node n. */ @Override public void visit(NodeTraversal t, Node n, Node parent) { JSType childType; JSType leftType, rightType; Node left, right; // To be explicitly set to false if the node is not typeable. boolean typeable = true; switch (n.getType()) { case Token.NAME: typeable = visitName(t, n, parent); break; case Token.PARAM_LIST: // If this is under a FUNCTION node, it is a parameter list and can be // ignored here. if (parent.getType() != Token.FUNCTION) { ensureTyped(t, n, getJSType(n.getFirstChild())); } else { typeable = false; } break; case Token.COMMA: ensureTyped(t, n, getJSType(n.getLastChild())); break; case Token.TRUE: case Token.FALSE: ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.THIS: ensureTyped(t, n, t.getScope().getTypeOfThis()); break; case Token.NULL: ensureTyped(t, n, NULL_TYPE); break; case Token.NUMBER: ensureTyped(t, n, NUMBER_TYPE); break; case Token.STRING: // Object literal keys are handled with OBJECTLIT if (!NodeUtil.isObjectLitKey(n, n.getParent())) { ensureTyped(t, n, STRING_TYPE); } else { // Object literal keys are not typeable typeable = false; } break; case Token.GETTER_DEF: case Token.SETTER_DEF: // Object literal keys are handled with OBJECTLIT break; case Token.ARRAYLIT: ensureTyped(t, n, ARRAY_TYPE); break; case Token.REGEXP: ensureTyped(t, n, REGEXP_TYPE); break; case Token.GETPROP: visitGetProp(t, n, parent); typeable = !(parent.isAssign() && parent.getFirstChild() ==

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> n); break; case Token.GETELEM: visitGetElem(t, n); // The type of GETELEM is always unknown, so no point counting that. // If that unknown leaks elsewhere (say by an assignment to another // variable), then it will be counted. typeable = false; break; case Token.VAR: visitVar(t, n); typeable = false; break; case Token.NEW: visitNew(t, n); typeable = true; break; case Token.CALL: visitCall(t, n); typeable = !NodeUtil.isExpressionNode(parent); break; case Token.RETURN: visitReturn(t, n); typeable = false; break; case Token.DEC: case Token.INC: left = n.getFirstChild(); validator.expectNumber( t, left, getJSType(left), "increment/decrement"); ensureTyped(t, n, NUMBER_TYPE); break; case Token.NOT: ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.VOID: ensureTyped(t, n, VOID_TYPE); break; case Token.TYPEOF: ensureTyped(t, n, STRING_TYPE); break; case Token.BITNOT: childType = getJSType(n.getFirstChild()); if (!childType.matchesInt32Context()) { report(t, n, BIT_OPERATION, NodeUtil.opToStr(n.getType()), childType.toString()); } ensureTyped(t, n, NUMBER_TYPE); break; case Token.POS: case Token.NEG: left = n.getFirstChild(); validator.expectNumber(t, left, getJSType(left), "sign operator"); ensureTyped(t, n, NUMBER_TYPE); break; case Token.EQ: case Token.NE: { leftType = getJSType(n.getFirstChild()); rightType = getJSType(n.getLastChild()); JSType leftTypeRestricted = leftType.restrictByNotNullOrUndefined(); JSType rightTypeRestricted = rightType.restrictByNotNullOrUndefined(); TernaryValue result = leftTypeRestricted.testForEquality(rightTypeRestricted); if (result != TernaryValue.UNKNOWN) { if (n.getType() == Token.NE) { result = result.not(); } report(t, n, DETERMINISTIC_TEST, leftType.toString(), rightType.toString(), result.toString()); } ensureTyped(t, n, BOOLEAN_TYPE); break; } case Token.SHEQ: case Token.SHNE: { leftType = getJSType(n.getFirstChild()); rightType = getJSType(n.getLastChild()); JSType

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> leftTypeRestricted = leftType.restrictByNotNullOrUndefined(); JSType rightTypeRestricted = rightType.restrictByNotNullOrUndefined(); if (!leftTypeRestricted.canTestForShallowEqualityWith( rightTypeRestricted)) { report(t, n, DETERMINISTIC_TEST_NO_RESULT, leftType.toString(), rightType.toString()); } ensureTyped(t, n, BOOLEAN_TYPE); break; } case Token.LT: case Token.LE: case Token.GT: case Token.GE: leftType = getJSType(n.getFirstChild()); rightType = getJSType(n.getLastChild()); if (rightType.isNumber()) { validator.expectNumber( t, n, leftType, "left side of numeric comparison"); } else if (leftType.isNumber()) { validator.expectNumber( t, n, rightType, "right side of numeric comparison"); } else if (leftType.matchesNumberContext() && rightType.matchesNumberContext()) { // OK. } else { // Whether the comparison is numeric will be determined at runtime // each time the expression is evaluated. Regardless, both operands // should match a string context. String message = "left side of comparison"; validator.expectString(t, n, leftType, message); validator.expectNotNullOrUndefined( t, n, leftType, message, getNativeType(STRING_TYPE)); message = "right side of comparison"; validator.expectString(t, n, rightType, message); validator.expectNotNullOrUndefined( t, n, rightType, message, getNativeType(STRING_TYPE)); } ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.IN: left = n.getFirstChild(); right = n.getLastChild(); leftType = getJSType(left); rightType = getJSType(right); validator.expectObject(t, n, rightType, "'in' requires an object"); validator.expectString(t, left, leftType, "left side of 'in'"); ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.INSTANCEOF: left = n.getFirstChild(); right = n.getLastChild(); leftType = getJSType(left); rightType = getJSType(right).restrictByNotNullOrUndefined(); validator.expectAnyObject( t, left, leftType, "deterministic instanceof yields false"); validator.expectActualObject( t, right, rightType, "instanceof requires an object"); ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.ASSIGN: visitAssign(t, n); typeable = false; break; case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>.ASSIGN_URSH: case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITAND: case Token.ASSIGN_SUB: case Token.ASSIGN_ADD: case Token.ASSIGN_MUL: case Token.LSH: case Token.RSH: case Token.URSH: case Token.DIV: case Token.MOD: case Token.BITOR: case Token.BITXOR: case Token.BITAND: case Token.SUB: case Token.ADD: case Token.MUL: visitBinaryOperator(n.getType(), t, n); break; case Token.DELPROP: ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.CASE: JSType switchType = getJSType(parent.getFirstChild()); JSType caseType = getJSType(n.getFirstChild()); validator.expectSwitchMatchesCase(t, n, switchType, caseType); typeable = false; break; case Token.WITH: { Node child = n.getFirstChild(); childType = getJSType(child); validator.expectObject( t, child, childType, "with requires an object"); typeable = false; break; } case Token.FUNCTION: visitFunction(t, n); break; // These nodes have no interesting type behavior. case Token.LABEL: case Token.LABEL_NAME: case Token.SWITCH: case Token.BREAK: case Token.CATCH: case Token.TRY: case Token.SCRIPT: case Token.EXPR_RESULT: case Token.BLOCK: case Token.EMPTY: case Token.DEFAULT_CASE: case Token.CONTINUE: case Token.DEBUGGER: case Token.THROW: typeable = false; break; // These nodes require data flow analysis. case Token.DO: case Token.FOR: case Token.IF: case Token.WHILE: typeable = false; break; // These nodes are typed during the type inference. case Token.AND: case Token.HOOK: case Token.OBJECTLIT: case Token.OR: if (n.getJSType() != null) { // If we didn't run type inference. ensureTyped(t, n); } else { // If this is an enum, then give that type to the objectlit as well. if ((n.isObjectLit()) && (parent.getJSType() instanceof EnumType)) { ensureTyped(t, n, parent.getJSType()); } else { ensureTyped(t, n); } } if (n.isObjectLit()) { for (Node key : n.children()) { visitObjLitKey(t,

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> key, n); } } break; default: report(t, n, UNEXPECTED_TOKEN, Token.name(n.getType())); ensureTyped(t, n); break; } // Don't count externs since the user's code may not even use that part. typeable = typeable && !inExterns; if (typeable) { doPercentTypedAccounting(t, n); } checkNoTypeCheckSection(n, false); } /** * Counts the given node in the typed statistics. * @param n a node that should be typed */ private void doPercentTypedAccounting(NodeTraversal t, Node n) { JSType type = n.getJSType(); if (type == null) { nullCount++; } else if (type.isUnknownType()) { if (reportUnknownTypes.isOn()) { compiler.report( t.makeError(n, reportUnknownTypes, UNKNOWN_EXPR_TYPE)); } unknownCount++; } else { typedCount++; } } /** * Visits an assignment <code>lvalue = rvalue</code>. If the * <code>lvalue</code> is a prototype modification, we change the schema * of the object type it is referring to. * @param t the traversal * @param assign the assign node * (<code>assign.isAssign()</code> is an implicit invariant) */ private void visitAssign(NodeTraversal t, Node assign) { JSDocInfo info = assign.getJSDocInfo(); Node lvalue = assign.getFirstChild(); Node rvalue = assign.getLastChild(); if (lvalue.isGetProp()) { Node object = lvalue.getFirstChild(); JSType objectJsType = getJSType(object); String property = lvalue.getLastChild().getString(); // the first name in this getprop refers to an interface // we perform checks in addition to the ones below if (object.isGetProp()) { JSType jsType = getJSType(object.getFirstChild()); if (jsType.isInterface() && object.getLastChild().getString().equals("prototype")) { visitInterfaceGetprop(t, assign, object, property, lvalue, rvalue); } } // /** @type ... */object.name = ...; if (info != null && info.hasType()) { visitAnnotatedAssignGetprop(t, assign, info.getType().evaluate(t.getScope(), typeRegistry), object, property, rvalue); return; } checkEnumAlias(t, info, rvalue); // object.prototype = ...; if (property.equals("prototype")) { if (objectJsType != null && objectJsType.isFunctionType()) { FunctionType functionType = objectJsType.toMaybeFunctionType

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>. // For getter and setter property definitions the // rvalue type != the property type. Node rvalue = key.getFirstChild(); JSType rightType = NodeUtil.getObjectLitKeyTypeFromValueType( key, getJSType(rvalue)); if (rightType == null) { rightType = getNativeType(UNKNOWN_TYPE); } Node owner = objlit; // Validate value is assignable to the key type. JSType keyType = getJSType(key); JSType allowedValueType = keyType; if (allowedValueType.isEnumElementType()) { allowedValueType = allowedValueType.toMaybeEnumElementType().getPrimitiveType(); } boolean valid = validator.expectCanAssignToPropertyOf(t, key, rightType, allowedValueType, owner, NodeUtil.getObjectLitKeyName(key)); if (valid) { ensureTyped(t, key, rightType); } else { ensureTyped(t, key); } // Validate that the key type is assignable to the object property type. // This is necessary as the objlit may have been cast to a non-literal // object type. // TODO(johnlenz): consider introducing a CAST node to the AST (or // perhaps a parentheses node). JSType objlitType = getJSType(objlit); ObjectType type = ObjectType.cast( objlitType.restrictByNotNullOrUndefined()); if (type != null) { String property = NodeUtil.getObjectLitKeyName(key); if (type.hasProperty(property) && !type.isPropertyTypeInferred(property) && !propertyIsImplicitCast(type, property)) { validator.expectCanAssignToPropertyOf( t, key, keyType, type.getPropertyType(property), owner, property); } return; } } /** * Returns true if any type in the chain has an implictCast annotation for * the given property. */ private boolean propertyIsImplicitCast(ObjectType type, String prop) { for (; type != null; type = type.getImplicitPrototype()) { JSDocInfo docInfo = type.getOwnPropertyJSDocInfo(prop); if (docInfo != null && docInfo.isImplicitCast()) { return true; } } return false; } /** * Given a constructor type and a property name, check that the property has * the JSDoc annotation @override iff the property is declared on a * superclass. Several checks regarding inheritance correctness are also * performed. */ private void checkDeclaredPropertyInheritance( NodeTraversal t, Node n, FunctionType ctorType, String propertyName, JSDocInfo info, JSType propertyType) { // If the supertype doesn't resolve correctly, we've warned about this // already. if (hasUnknownOrEmptySupertype(ctorType)) { return; }

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> FunctionType superClass = ctorType.getSuperClassConstructor(); boolean superClassHasProperty = superClass != null && superClass.getPrototype().hasProperty(propertyName); // For interface boolean superInterfacesHasProperty = false; if (ctorType.isInterface()) { for (ObjectType interfaceType : ctorType.getExtendedInterfaces()) { superInterfacesHasProperty = superInterfacesHasProperty || interfaceType.hasProperty(propertyName); } } boolean declaredOverride = info != null && info.isOverride(); boolean foundInterfaceProperty = false; if (ctorType.isConstructor()) { for (JSType implementedInterface : ctorType.getAllImplementedInterfaces()) { if (implementedInterface.isUnknownType() || implementedInterface.isEmptyType()) { continue; } FunctionType interfaceType = implementedInterface.toObjectType().getConstructor(); Preconditions.checkNotNull(interfaceType); boolean interfaceHasProperty = interfaceType.getPrototype().hasProperty(propertyName); foundInterfaceProperty = foundInterfaceProperty || interfaceHasProperty; if (reportMissingOverride.isOn() && !declaredOverride && interfaceHasProperty) { // @override not present, but the property does override an interface // property compiler.report(t.makeError(n, reportMissingOverride, HIDDEN_INTERFACE_PROPERTY, propertyName, interfaceType.getTopMostDefiningType(propertyName).toString())); } } } if (!declaredOverride && !superClassHasProperty && !superInterfacesHasProperty) { // nothing to do here, it's just a plain new property return; } JSType topInstanceType = superClassHasProperty ? superClass.getTopMostDefiningType(propertyName) : null; if (reportMissingOverride.isOn() && ctorType.isConstructor() && !declaredOverride && superClassHasProperty) { // @override not present, but the property does override a superclass // property compiler.report(t.makeError(n, reportMissingOverride, HIDDEN_SUPERCLASS_PROPERTY, propertyName, topInstanceType.toString())); } if (!declaredOverride) { // there's no @override to check return; } // @override is present and we have to check that it is ok if (superClassHasProperty) { // there is a superclass implementation JSType superClassPropType = superClass.getPrototype().getPropertyType(propertyName); if (!propertyType.canAssignTo(superClassPropType)) { compiler.report( t.makeError(n, HIDDEN_SUPERCLASS_PROPERTY_MISMATCH, propertyName, topInstanceType.toString(), superClassPropType.toString(), propertyType.toString())); } } else if (superInterfacesHasProperty) { // there is an super interface property for (ObjectType interfaceType : ctorType.getExtendedInterfaces()) { if (interfaceType.hasProperty(propertyName)) { JSType superPropertyType = interfaceType

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>.getPropertyType(propertyName); if (!propertyType.canAssignTo(superPropertyType)) { topInstanceType = interfaceType.getConstructor(). getTopMostDefiningType(propertyName); compiler.report( t.makeError(n, HIDDEN_SUPERCLASS_PROPERTY_MISMATCH, propertyName, topInstanceType.toString(), superPropertyType.toString(), propertyType.toString())); } } } } else if (!foundInterfaceProperty) { // there is no superclass nor interface implementation compiler.report( t.makeError(n, UNKNOWN_OVERRIDE, propertyName, ctorType.getInstanceType().toString())); } } /** * Given a constructor or an interface type, find out whether the unknown * type is a supertype of the current type. */ private static boolean hasUnknownOrEmptySupertype(FunctionType ctor) { Preconditions.checkArgument(ctor.isConstructor() || ctor.isInterface()); Preconditions.checkArgument(!ctor.isUnknownType()); // The type system should notice inheritance cycles on its own // and break the cycle. while (true) { ObjectType maybeSuperInstanceType = ctor.getPrototype().getImplicitPrototype(); if (maybeSuperInstanceType == null) { return false; } if (maybeSuperInstanceType.isUnknownType() || maybeSuperInstanceType.isEmptyType()) { return true; } ctor = maybeSuperInstanceType.getConstructor(); if (ctor == null) { return false; } Preconditions.checkState(ctor.isConstructor() || ctor.isInterface()); } } /** * Visits an ASSIGN node for cases such as * <pre> * interface.property2.property = ...; * </pre> */ private void visitInterfaceGetprop(NodeTraversal t, Node assign, Node object, String property, Node lvalue, Node rvalue) { JSType rvalueType = getJSType(rvalue); // Only 2 values are allowed for methods: // goog.abstractMethod // function () {}; // or for properties, no assignment such as: // InterfaceFoo.prototype.foobar; String abstractMethodName = compiler.getCodingConvention().getAbstractMethodName(); if (!rvalueType.isOrdinaryFunction() && !(rvalue.isQualifiedName() && rvalue.getQualifiedName().equals(abstractMethodName))) { // This is bad i18n style but we don't localize our compiler errors. String abstractMethodMessage = (abstractMethodName != null) ? ", or " + abstractMethodName : ""; compiler.report( t.makeError(object, INVALID_INTERFACE_MEMBER_DECLARATION, abstractMethodMessage)); } if (assign.getLastChild().isFunction() && !NodeUtil.isEmptyBlock(assign.getLastChild().getLastChild())) { compiler.report( t.makeError(object,

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> INTERFACE_FUNCTION_NOT_EMPTY, abstractMethodName)); } } /** * Visits an ASSIGN node for cases such as * <pre> * object.property = ...; * </pre> * that have an {@code @type} annotation. */ private void visitAnnotatedAssignGetprop(NodeTraversal t, Node assign, JSType type, Node object, String property, Node rvalue) { // verifying that the rvalue has the correct type validator.expectCanAssignToPropertyOf(t, assign, getJSType(rvalue), type, object, property); } /** * Visits a NAME node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. * @param parent The parent of the node n. * @return whether the node is typeable or not */ boolean visitName(NodeTraversal t, Node n, Node parent) { // At this stage, we need to determine whether this is a leaf // node in an expression (which therefore needs to have a type // assigned for it) versus some other decorative node that we // can safely ignore. Function names, arguments (children of LP nodes) and // variable declarations are ignored. // TODO(user): remove this short-circuiting in favor of a // pre order traversal of the FUNCTION, CATCH, LP and VAR nodes. int parentNodeType = parent.getType(); if (parentNodeType == Token.FUNCTION || parentNodeType == Token.CATCH || parentNodeType == Token.PARAM_LIST || parentNodeType == Token.VAR) { return false; } JSType type = n.getJSType(); if (type == null) { type = getNativeType(UNKNOWN_TYPE); Var var = t.getScope().getVar(n.getString()); if (var != null) { JSType varType = var.getType(); if (varType != null) { type = varType; } } } ensureTyped(t, n, type); return true; } /** * Visits a GETPROP node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. * @param parent The parent of <code>n</code> */ private void visitGetProp(NodeTraversal t, Node n, Node parent) { // GETPROP nodes have an assigned type on their node by the scope creator // if this is an enum declaration. The only namespaced enum declarations // that we allow are of the form object.name = ...; if (n.getJSType() != null && parent.isAssign()) {

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> return; } // obj.prop or obj.method() // Lots of types can appear on the left, a call to a void function can // never be on the left. getPropertyType will decide what is acceptable // and what isn't. Node property = n.getLastChild(); Node objNode = n.getFirstChild(); JSType childType = getJSType(objNode); // TODO(user): remove in favor of flagging every property access on // non-object. if (!validator.expectNotNullOrUndefined(t, n, childType, "No properties on this expression", getNativeType(OBJECT_TYPE))) { ensureTyped(t, n); return; } checkPropertyAccess(childType, property.getString(), t, n); ensureTyped(t, n); } /** * Emit a warning if we can prove that a property cannot possibly be * defined on an object. Note the difference between JS and a strictly * statically typed language: we're checking if the property * *cannot be defined*, whereas a java compiler would check if the * property *can be undefined*. */ private void checkPropertyAccess(JSType childType, String propName, NodeTraversal t, Node n) { // If the property type is unknown, check the object type to see if it // can ever be defined. We explicitly exclude CHECKED_UNKNOWN (for // properties where we've checked that it exists, or for properties on // objects that aren't in this binary). JSType propType = getJSType(n); if (propType.equals(typeRegistry.getNativeType(UNKNOWN_TYPE))) { childType = childType.autobox(); ObjectType objectType = ObjectType.cast(childType); if (objectType != null) { // We special-case object types so that checks on enums can be // much stricter, and so that we can use hasProperty (which is much // faster in most cases). if (!objectType.hasProperty(propName) || objectType.equals(typeRegistry.getNativeType(UNKNOWN_TYPE))) { if (objectType instanceof EnumType) { report(t, n, INEXISTENT_ENUM_ELEMENT, propName); } else { checkPropertyAccessHelper(objectType, propName, t, n); } } } else { checkPropertyAccessHelper(childType, propName, t, n); } } } private void checkPropertyAccessHelper(JSType objectType, String propName, NodeTraversal t, Node n) { if (!objectType.isEmptyType() && reportMissingProperties && !isPropertyTest(n)) { if (!typeRegistry.canPropertyBeDefined(objectType, propName)) { report(t, n, INEXISTENT_PROPERTY, propName, validator.getReadableJSTypeName(n.getFirst

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>Child(), true)); } } } /** * Determines whether this node is testing for the existence of a property. * If true, we will not emit warnings about a missing property. * * @param getProp The GETPROP being tested. */ private boolean isPropertyTest(Node getProp) { Node parent = getProp.getParent(); switch (parent.getType()) { case Token.CALL: return parent.getFirstChild() != getProp && compiler.getCodingConvention().isPropertyTestFunction(parent); case Token.IF: case Token.WHILE: case Token.DO: case Token.FOR: return NodeUtil.getConditionExpression(parent) == getProp; case Token.INSTANCEOF: case Token.TYPEOF: return true; case Token.AND: case Token.HOOK: return parent.getFirstChild() == getProp; case Token.NOT: return parent.getParent().isOr() && parent.getParent().getFirstChild() == parent; } return false; } /** * Visits a GETELEM node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. */ private void visitGetElem(NodeTraversal t, Node n) { Node left = n.getFirstChild(); Node right = n.getLastChild(); validator.expectIndexMatch(t, n, getJSType(left), getJSType(right)); ensureTyped(t, n); } /** * Visits a VAR node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. */ private void visitVar(NodeTraversal t, Node n) { // TODO(nicksantos): Fix this so that the doc info always shows up // on the NAME node. We probably want to wait for the parser // merge to fix this. JSDocInfo varInfo = n.hasOneChild() ? n.getJSDocInfo() : null; for (Node name : n.children()) { Node value = name.getFirstChild(); // A null var would indicate a bug in the scope creation logic. Var var = t.getScope().getVar(name.getString()); if (value != null) { JSType valueType = getJSType(value); JSType nameType = var.getType(); nameType = (nameType == null) ? getNativeType(UNKNOWN_TYPE) : nameType; JSDocInfo info = name.getJSDocInfo(); if (info == null) { info = varInfo; } checkEnumAlias(t, info, value);

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> if (var.isTypeInferred()) { ensureTyped(t, name, valueType); } else { validator.expectCanAssignTo( t, value, valueType, nameType, "initializing variable"); } } } } /** * Visits a NEW node. */ private void visitNew(NodeTraversal t, Node n) { Node constructor = n.getFirstChild(); JSType type = getJSType(constructor).restrictByNotNullOrUndefined(); if (type.isConstructor() || type.isEmptyType() || type.isUnknownType()) { FunctionType fnType = type.toMaybeFunctionType(); if (fnType != null) { visitParameterList(t, n, fnType); ensureTyped(t, n, fnType.getInstanceType()); } else { ensureTyped(t, n); } } else { report(t, n, NOT_A_CONSTRUCTOR); ensureTyped(t, n); } } /** * Check whether there's any property conflict for for a particular super * interface * @param t The node traversal object that supplies context * @param n The node being visited * @param functionName The function name being checked * @param properties The property names in the super interfaces that have * been visited * @param currentProperties The property names in the super interface * that have been visited * @param interfaceType The super interface that is being visited */ private void checkInterfaceConflictProperties(NodeTraversal t, Node n, String functionName, HashMap<String, ObjectType> properties, HashMap<String, ObjectType> currentProperties, ObjectType interfaceType) { Set<String> currentPropertyNames = interfaceType.getPropertyNames(); for (String name : currentPropertyNames) { ObjectType oType = properties.get(name); if (oType != null) { if (!interfaceType.getPropertyType(name).isEquivalentTo( oType.getPropertyType(name))) { compiler.report( t.makeError(n, INCOMPATIBLE_EXTENDED_PROPERTY_TYPE, functionName, name, oType.toString(), interfaceType.toString())); } } currentProperties.put(name, interfaceType); } for (ObjectType iType : interfaceType.getCtorExtendedInterfaces()) { checkInterfaceConflictProperties(t, n, functionName, properties, currentProperties, iType); } } /** * Visits a {@link Token#FUNCTION} node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType functionType = JSType.toMaybeFunctionType(n.getJSType()); String functionPrivateName =

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> n.getFirstChild().getString(); if (functionType.isConstructor()) { FunctionType baseConstructor = functionType.getSuperClassConstructor(); if (baseConstructor != null && baseConstructor != getNativeType(OBJECT_FUNCTION_TYPE) && (baseConstructor.isInterface() && functionType.isConstructor())) { compiler.report( t.makeError(n, CONFLICTING_EXTENDED_TYPE, functionPrivateName)); } else { // All interfaces are properly implemented by a class for (JSType baseInterface : functionType.getImplementedInterfaces()) { boolean badImplementedType = false; ObjectType baseInterfaceObj = ObjectType.cast(baseInterface); if (baseInterfaceObj != null) { FunctionType interfaceConstructor = baseInterfaceObj.getConstructor(); if (interfaceConstructor != null && !interfaceConstructor.isInterface()) { badImplementedType = true; } } else { badImplementedType = true; } if (badImplementedType) { report(t, n, BAD_IMPLEMENTED_TYPE, functionPrivateName); } } // check properties validator.expectAllInterfaceProperties(t, n, functionType); } } else if (functionType.isInterface()) { // Interface must extend only interfaces for (ObjectType extInterface : functionType.getExtendedInterfaces()) { if (extInterface.getConstructor() != null && !extInterface.getConstructor().isInterface()) { compiler.report( t.makeError(n, CONFLICTING_EXTENDED_TYPE, functionPrivateName)); } } // Interface cannot implement any interfaces if (functionType.hasImplementedInterfaces()) { compiler.report(t.makeError(n, CONFLICTING_IMPLEMENTED_TYPE, functionPrivateName)); } // Check whether the extended interfaces have any conflicts if (functionType.getExtendedInterfacesCount() > 1) { // Only check when extending more than one interfaces HashMap<String, ObjectType> properties = new HashMap<String, ObjectType>(); HashMap<String, ObjectType> currentProperties = new HashMap<String, ObjectType>(); for (ObjectType interfaceType : functionType.getExtendedInterfaces()) { currentProperties.clear(); checkInterfaceConflictProperties(t, n, functionPrivateName, properties, currentProperties, interfaceType); properties.putAll(currentProperties); } } } } /** * Visits a CALL node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. */ private void visitCall(NodeTraversal t, Node n) { Node child = n.getFirstChild(); JSType childType = getJSType(child).restrictByNotNullOrUndefined(); if (!childType.canBeCalled()) { report(t,

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> n, NOT_CALLABLE, childType.toString()); ensureTyped(t, n); return; } // A couple of types can be called as if they were functions. // If it is a function type, then validate parameters. if (childType.isFunctionType()) { FunctionType functionType = childType.toMaybeFunctionType(); boolean isExtern = false; JSDocInfo functionJSDocInfo = functionType.getJSDocInfo(); if( functionJSDocInfo != null && functionJSDocInfo.getAssociatedNode() != null) { isExtern = functionJSDocInfo.getAssociatedNode().isFromExterns(); } // Non-native constructors should not be called directly // unless they specify a return type and are defined // in an extern. if (functionType.isConstructor() && !functionType.isNativeObjectType() && (functionType.getReturnType().isUnknownType() || functionType.getReturnType().isVoidType() || !isExtern)) { report(t, n, CONSTRUCTOR_NOT_CALLABLE, childType.toString()); } // Functions with explcit 'this' types must be called in a GETPROP // or GETELEM. if (functionType.isOrdinaryFunction() && !functionType.getTypeOfThis().isUnknownType() && !functionType.getTypeOfThis().isNativeObjectType() && !(child.isGetElem() || child.isGetProp())) { report(t, n, EXPECTED_THIS_TYPE, functionType.toString()); } visitParameterList(t, n, functionType); ensureTyped(t, n, functionType.getReturnType()); } else { ensureTyped(t, n); } // TODO: Add something to check for calls of RegExp objects, which is not // supported by IE. Either say something about the return type or warn // about the non-portability of the call or both. } /** * Visits the parameters of a CALL or a NEW node. */ private void visitParameterList(NodeTraversal t, Node call, FunctionType functionType) { Iterator<Node> arguments = call.children().iterator(); arguments.next(); // skip the function name Iterator<Node> parameters = functionType.getParameters().iterator(); int ordinal = 0; Node parameter = null; Node argument = null; while (arguments.hasNext() && (parameters.hasNext() || parameter != null && parameter.isVarArgs())) { // If there are no parameters left in the list, then the while loop // above implies that this must be a var_args function. if (parameters.hasNext()) { parameter = parameters.next(); } argument = arguments.next(); ordinal++; validator.expectArgumentMatchesParameter(t, argument, getJSType(

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>argument), getJSType(parameter), call, ordinal); } int numArgs = call.getChildCount() - 1; int minArgs = functionType.getMinArguments(); int maxArgs = functionType.getMaxArguments(); if (minArgs > numArgs || maxArgs < numArgs) { report(t, call, WRONG_ARGUMENT_COUNT, validator.getReadableJSTypeName(call.getFirstChild(), false), String.valueOf(numArgs), String.valueOf(minArgs), maxArgs != Integer.MAX_VALUE ? " and no more than " + maxArgs + " argument(s)" : ""); } } /** * Visits a RETURN node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. */ private void visitReturn(NodeTraversal t, Node n) { Node function = t.getEnclosingFunction(); // This is a misplaced return, but the real JS will fail to compile, // so let it go. if (function == null) { return; } JSType jsType = getJSType(function); if (jsType.isFunctionType()) { FunctionType functionType = jsType.toMaybeFunctionType(); JSType returnType = functionType.getReturnType(); // if no return type is specified, undefined must be returned // (it's a void function) if (returnType == null) { returnType = getNativeType(VOID_TYPE); } // fetching the returned value's type Node valueNode = n.getFirstChild(); JSType actualReturnType; if (valueNode == null) { actualReturnType = getNativeType(VOID_TYPE); valueNode = n; } else { actualReturnType = getJSType(valueNode); } // verifying validator.expectCanAssignTo(t, valueNode, actualReturnType, returnType, "inconsistent return type"); } } /** * This function unifies the type checking involved in the core binary * operators and the corresponding assignment operators. The representation * used internally is such that common code can handle both kinds of * operators easily. * * @param op The operator. * @param t The traversal object, needed to report errors. * @param n The node being checked. */ private void visitBinaryOperator(int op, NodeTraversal t, Node n) { Node left = n.getFirstChild(); JSType leftType = getJSType(left); Node right = n.getLastChild(); JSType rightType = getJSType(right); switch (op) { case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.LSH: case Token.RSH: case Token.ASSIGN_URSH:

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> case Token.URSH: if (!leftType.matchesInt32Context()) { report(t, left, BIT_OPERATION, NodeUtil.opToStr(n.getType()), leftType.toString()); } if (!rightType.matchesUint32Context()) { report(t, right, BIT_OPERATION, NodeUtil.opToStr(n.getType()), rightType.toString()); } break; case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: case Token.ASSIGN_MUL: case Token.ASSIGN_SUB: case Token.DIV: case Token.MOD: case Token.MUL: case Token.SUB: validator.expectNumber(t, left, leftType, "left operand"); validator.expectNumber(t, right, rightType, "right operand"); break; case Token.ASSIGN_BITAND: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITOR: case Token.BITAND: case Token.BITXOR: case Token.BITOR: validator.expectBitwiseable(t, left, leftType, "bad left operand to bitwise operator"); validator.expectBitwiseable(t, right, rightType, "bad right operand to bitwise operator"); break; case Token.ASSIGN_ADD: case Token.ADD: break; default: report(t, n, UNEXPECTED_TOKEN, Node.tokenToName(op)); } ensureTyped(t, n); } /** * <p>Checks enum aliases. * * <p>We verify that the enum element type of the enum used * for initialization is a subtype of the enum element type of * the enum the value is being copied in.</p> * * <p>Example:</p> * <pre>var myEnum = myOtherEnum;</pre> * * <p>Enum aliases are irregular, so we need special code for this :(</p> * * @param value the value used for initialization of the enum */ private void checkEnumAlias( NodeTraversal t, JSDocInfo declInfo, Node value) { if (declInfo == null || !declInfo.hasEnumParameterType()) { return; } JSType valueType = getJSType(value); if (!valueType.isEnumType()) { return; } EnumType valueEnumType = valueType.toMaybeEnumType(); JSType valueEnumPrimitiveType = valueEnumType.getElementsType().getPrimitiveType(); validator.expectCanAssignTo(t, value, valueEnumPrimitiveType, declInfo.getEnumParameterType().evaluate(t.getScope(), typeRegistry), "incompatible enum element types"); } /** * This method gets the JSType from the Node argument and verifies that it is * present. */

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> private JSType getJSType(Node n) { JSType jsType = n.getJSType(); if (jsType == null) { // TODO(nicksantos): This branch indicates a compiler bug, not worthy of // halting the compilation but we should log this and analyze to track // down why it happens. This is not critical and will be resolved over // time as the type checker is extended. return getNativeType(UNKNOWN_TYPE); } else { return jsType; } } // TODO(nicksantos): TypeCheck should never be attaching types to nodes. // All types should be attached by TypeInference. This is not true today // for legacy reasons. There are a number of places where TypeInference // doesn't attach a type, as a signal to TypeCheck that it needs to check // that node's type. /** * Ensure that the given node has a type. If it does not have one, * attach the UNKNOWN_TYPE. */ private void ensureTyped(NodeTraversal t, Node n) { ensureTyped(t, n, getNativeType(UNKNOWN_TYPE)); } private void ensureTyped(NodeTraversal t, Node n, JSTypeNative type) { ensureTyped(t, n, getNativeType(type)); } /** * Enforces type casts, and ensures the node is typed. * * A cast in the way that we use it in JSDoc annotations never * alters the generated code and therefore never can induce any runtime * operation. What this means is that a 'cast' is really just a compile * time constraint on the underlying value. In the future, we may add * support for run-time casts for compiled tests. * * To ensure some shred of sanity, we enforce the notion that the * type you are casting to may only meaningfully be a narrower type * than the underlying declared type. We also invalidate optimizations * on bad type casts. * * @param t The traversal object needed to report errors. * @param n The node getting a type assigned to it. * @param type The type to be assigned. */ private void ensureTyped(NodeTraversal t, Node n, JSType type) { // Make sure FUNCTION nodes always get function type. Preconditions.checkState(n.getType() != Token.FUNCTION || type.isFunctionType() || type.isUnknownType()); JSDocInfo info = n.getJSDocInfo(); if (info != null) { if (info.hasType()) { JSType infoType = info.getType().evaluate(t.getScope(), typeRegistry); validator.expectCanCast(t, n, infoType, type); type = infoType; } if (info.isImplicitCast() && !inExterns) { String propName = n

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>.isGetProp() ? n.getLastChild().getString() : "(missing)"; compiler.report( t.makeError(n, ILLEGAL_IMPLICIT_CAST, propName)); } } if (n.getJSType() == null) { n.setJSType(type); } } /** * Returns the percentage of nodes typed by the type checker. * @return a number between 0.0 and 100.0 */ double getTypedPercent() { int total = nullCount + unknownCount + typedCount; if (total == 0) { return 0.0; } else { return (100.0 * typedCount) / total; } } private JSType getNativeType(JSTypeNative typeId) { return typeRegistry.getNativeType(typeId); } }

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>/* * Copyright 2010 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.jscomp.regex.RegExpTree; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.Node; /** * Look for references to the global RegExp object that would cause * regular expressions to be unoptimizable, and checks that regular expressions * are syntactically valid. * * @author johnlenz@google.com (John Lenz) */ class CheckRegExp extends AbstractPostOrderCallback implements CompilerPass { static final DiagnosticType REGEXP_REFERENCE = DiagnosticType.warning("JSC_REGEXP_REFERENCE", "References to the global RegExp object prevents " + "optimization of regular expressions."); static final DiagnosticType MALFORMED_REGEXP = DiagnosticType.warning( "JSC_MALFORMED_REGEXP", "Malformed Regular Expression: {0}"); private final AbstractCompiler compiler; private boolean globalRegExpPropertiesUsed = false; public boolean isGlobalRegExpPropertiesUsed() { return globalRegExpPropertiesUsed; } public CheckRegExp(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isReferenceName(n)) { String name = n.getString(); if (name.equals("RegExp") && t.getScope().getVar(name) == null) { int parentType = parent.getType(); boolean first = (n == parent.getFirstChild()); if (!((parentType == Token.NEW && first) || (parentType == Token.CALL && first) || (parentType == Token.INSTANCEOF && !first))) { t.report(n, REGEXP_REFERENCE); globalRegExpPropertiesUsed = true; } } // Check the syntax of regular expression patterns. } else if (n.getType() == Token.REGEXP) { String pattern = n.getFirstChild().getString

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>(); String flags = n.getChildCount() == 2 ? n.getLastChild().getString() : ""; try { RegExpTree.parseRegExp(pattern, flags); } catch (IllegalArgumentException ex) { t.report(n, MALFORMED_REGEXP, ex.getMessage()); } } } }

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>irNode.getLineno() == -1) { // If we didn't already set the line, then set it now. This avoids // cases like ParenthesizedExpression where we just return a previous // node, but don't want the new node to get its parent's line number. int lineno = node.getLineno(); irNode.setLineno(lineno); int charno = position2charno(node.getAbsolutePosition()); irNode.setCharno(charno); maybeSetLengthFrom(irNode, node); } } /** * Creates a JsDocInfoParser and parses the JsDoc string. * * Used both for handling individual JSDoc comments and for handling * file-level JSDoc comments (@fileoverview and @license). * * @param node The JsDoc Comment node to parse. * @param irNode * @return A JSDocInfoParser. Will contain either fileoverview jsdoc, or * normal jsdoc, or no jsdoc (if the method parses to the wrong level). */ private JsDocInfoParser createJsDocInfoParser(Comment node, Node irNode) { String comment = node.getValue(); int lineno = node.getLineno(); int position = node.getAbsolutePosition(); // The JsDocInfoParser expects the comment without the initial '/**'. int numOpeningChars = 3; JsDocInfoParser jsdocParser = new JsDocInfoParser( new JsDocTokenStream(comment.substring(numOpeningChars), lineno, position2charno(position) + numOpeningChars), node, irNode, config, errorReporter); jsdocParser.setFileLevelJsDocBuilder(fileLevelJsDocBuilder); jsdocParser.setFileOverviewJSDocInfo(fileOverviewInfo); jsdocParser.parse(); return jsdocParser; } // Set the length on the node if we're in IDE mode. private void maybeSetLengthFrom(Node node, AstNode source) { if (config.isIdeMode) { node.setLength(source.getLength()); } } private int position2charno(int position) { int lineIndex = sourceString.lastIndexOf('\n', position); if (lineIndex == -1) { return position; } else { // Subtract one for initial position being 0. return position - lineIndex - 1; } } private Node justTransform(AstNode node) { return transformDispatcher.process(node); } private class TransformDispatcher extends TypeSafeDispatcher<Node> { private Node processGeneric( com.google.javascript.jscomp.mozilla.rhino.Node n) { Node node = newNode(transformTokenType(n.getType())); for (com.google.javascript.jscomp.mozilla.rhino.Node child : n) {

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> node.addChildToBack(transform((AstNode)child)); } return node; } /** * Transforms the given node and then sets its type to Token.STRING if it * was Token.NAME. If its type was already Token.STRING, then quotes it. * Used for properties, as the old AST uses String tokens, while the new one * uses Name tokens for unquoted strings. For example, in * var o = {'a' : 1, b: 2}; * the string 'a' is quoted, while the name b is turned into a string, but * unquoted. */ private Node transformAsString(AstNode n) { Node ret; if (n instanceof Name) { ret = transformNameAsString((Name)n); } else if (n instanceof NumberLiteral) { ret = transformNumberAsString((NumberLiteral)n); ret.putBooleanProp(Node.QUOTED_PROP, true); } else { ret = transform(n); ret.putBooleanProp(Node.QUOTED_PROP, true); } Preconditions.checkState(ret.isString()); return ret; } @Override Node processArrayLiteral(ArrayLiteral literalNode) { if (literalNode.isDestructuring()) { reportDestructuringAssign(literalNode); } Node node = newNode(Token.ARRAYLIT); for (AstNode child : literalNode.getElements()) { Node c = transform(child); node.addChildToBack(c); } return node; } @Override Node processAssignment(Assignment assignmentNode) { Node assign = processInfixExpression(assignmentNode); Node target = assign.getFirstChild(); if (!validAssignmentTarget(target)) { errorReporter.error( "invalid assignment target", sourceName, target.getLineno(), "", 0); } return assign; } @Override Node processAstRoot(AstRoot rootNode) { Node node = newNode(Token.SCRIPT); for (com.google.javascript.jscomp.mozilla.rhino.Node child : rootNode) { node.addChildToBack(transform((AstNode)child)); } parseDirectives(node); return node; } /** * Parse the directives, encode them in the AST, and remove their nodes. * * For information on ES5 directives, see section 14.1 of * Ecma-262, Edition 5. * * It would be nice if Rhino would eventually take care of this for * us, but right now their directive-processing is a one-off. */ private void parseDirectives(Node node) { // Remove all the directives, and encode them in the AST. Set<String> directives = null; while (isDirective(node.getFirstChild())) { String directive = node.remove

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>FirstChild().getFirstChild().getString(); if (directives == null) { directives = Sets.newHashSet(directive); } else { directives.add(directive); } } if (directives != null) { node.setDirectives(directives); } } private boolean isDirective(Node n) { if (n == null) return false; int nType = n.getType(); return nType == Token.EXPR_RESULT && n.getFirstChild().isString() && ALLOWED_DIRECTIVES.contains(n.getFirstChild().getString()); } @Override Node processBlock(Block blockNode) { return processGeneric(blockNode); } @Override Node processBreakStatement(BreakStatement statementNode) { Node node = newNode(Token.BREAK); if (statementNode.getBreakLabel() != null) { Node labelName = transform(statementNode.getBreakLabel()); // Change the NAME to LABEL_NAME labelName.setType(Token.LABEL_NAME); node.addChildToBack(labelName); } return node; } @Override Node processCatchClause(CatchClause clauseNode) { AstNode catchVar = clauseNode.getVarName(); Node node = newNode(Token.CATCH, transform(catchVar)); if (clauseNode.getCatchCondition() != null) { errorReporter.error( "Catch clauses are not supported", sourceName, clauseNode.getCatchCondition().getLineno(), "", 0); } node.addChildToBack(transformBlock(clauseNode.getBody())); return node; } @Override Node processConditionalExpression(ConditionalExpression exprNode) { return newNode( Token.HOOK, transform(exprNode.getTestExpression()), transform(exprNode.getTrueExpression()), transform(exprNode.getFalseExpression())); } @Override Node processContinueStatement(ContinueStatement statementNode) { Node node = newNode(Token.CONTINUE); if (statementNode.getLabel() != null) { Node labelName = transform(statementNode.getLabel()); // Change the NAME to LABEL_NAME labelName.setType(Token.LABEL_NAME); node.addChildToBack(labelName); } return node; } @Override Node processDoLoop(DoLoop loopNode) { return newNode( Token.DO, transformBlock(loopNode.getBody()), transform(loopNode.getCondition())); } @Override Node processElementGet(ElementGet getNode) { return newNode( Token.GETELEM, transform(getNode.getTarget()), transform(getNode.getElement())); } @Override Node processEmptyExpression(EmptyExpression exprNode) { Node node = newNode(Token.EMPTY); return node; } @Override Node processExpressionStatement(ExpressionStatement statementNode) { Node node = newNode(transformTokenType(statementNode.

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> there's no // function name, assume the paren was on the same line as the function. // TODO(bowdidge): Mark line number of paren correctly. Name fnName = functionNode.getFunctionName(); if (fnName != null) { lp.setLineno(fnName.getLineno()); } else { lp.setLineno(functionNode.getLineno()); } int lparenCharno = functionNode.getLp() + functionNode.getAbsolutePosition(); lp.setCharno(position2charno(lparenCharno)); for (AstNode param : functionNode.getParams()) { lp.addChildToBack(transform(param)); } node.addChildToBack(lp); Node bodyNode = transform(functionNode.getBody()); parseDirectives(bodyNode); node.addChildToBack(bodyNode); return node; } @Override Node processIfStatement(IfStatement statementNode) { Node node = newNode(Token.IF); node.addChildToBack(transform(statementNode.getCondition())); node.addChildToBack(transformBlock(statementNode.getThenPart())); if (statementNode.getElsePart() != null) { node.addChildToBack(transformBlock(statementNode.getElsePart())); } return node; } @Override Node processInfixExpression(InfixExpression exprNode) { Node n = newNode( transformTokenType(exprNode.getType()), transform(exprNode.getLeft()), transform(exprNode.getRight())); n.setLineno(exprNode.getLineno()); n.setCharno(position2charno(exprNode.getAbsolutePosition())); maybeSetLengthFrom(n, exprNode); return n; } @Override Node processKeywordLiteral(KeywordLiteral literalNode) { return newNode(transformTokenType(literalNode.getType())); } @Override Node processLabel(Label labelNode) { return newStringNode(Token.LABEL_NAME, labelNode.getName()); } @Override Node processLabeledStatement(LabeledStatement statementNode) { Node node = newNode(Token.LABEL); Node prev = null; Node cur = node; for (Label label : statementNode.getLabels()) { if (prev != null) { prev.addChildToBack(cur); } cur.addChildToBack(transform(label)); cur.setLineno(label.getLineno()); maybeSetLengthFrom(cur, label); int clauseAbsolutePosition = position2charno(label.getAbsolutePosition()); cur.setCharno(clauseAbsolutePosition); prev = cur; cur = newNode(Token.LABEL); } prev.addChildToBack(transform(statementNode.getStatement())); return node; } @Override Node processName(Name nameNode) {

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>, Boolean.TRUE); return node; } @Override Node processPropertyGet(PropertyGet getNode) { return newNode( Token.GETPROP, transform(getNode.getTarget()), transformAsString(getNode.getProperty())); } @Override Node processRegExpLiteral(RegExpLiteral literalNode) { Node literalStringNode = newStringNode(literalNode.getValue()); // assume it's on the same line. literalStringNode.setLineno(literalNode.getLineno()); maybeSetLengthFrom(literalStringNode, literalNode); Node node = newNode(Token.REGEXP, literalStringNode); String flags = literalNode.getFlags(); if (flags != null && !flags.isEmpty()) { Node flagsNode = newStringNode(flags); // Assume the flags are on the same line as the literal node. flagsNode.setLineno(literalNode.getLineno()); maybeSetLengthFrom(flagsNode, literalNode); node.addChildToBack(flagsNode); } return node; } @Override Node processReturnStatement(ReturnStatement statementNode) { Node node = newNode(Token.RETURN); if (statementNode.getReturnValue() != null) { node.addChildToBack(transform(statementNode.getReturnValue())); } return node; } @Override Node processScope(Scope scopeNode) { return processGeneric(scopeNode); } @Override Node processStringLiteral(StringLiteral literalNode) { Node n = newStringNode(literalNode.getValue()); return n; } @Override Node processSwitchCase(SwitchCase caseNode) { Node node; if (caseNode.isDefault()) { node = newNode(Token.DEFAULT_CASE); } else { AstNode expr = caseNode.getExpression(); node = newNode(Token.CASE, transform(expr)); } Node block = newNode(Token.BLOCK); block.putBooleanProp(Node.SYNTHETIC_BLOCK_PROP, true); block.setLineno(caseNode.getLineno()); block.setCharno(position2charno(caseNode.getAbsolutePosition())); maybeSetLengthFrom(block, caseNode); if (caseNode.getStatements() != null) { for (AstNode child : caseNode.getStatements()) { block.addChildToBack(transform(child)); } } node.addChildToBack(block); return node; } @Override Node processSwitchStatement(SwitchStatement statementNode) { Node node = newNode(Token.SWITCH, transform(statementNode.getExpression())); for (AstNode child : statementNode.getCases()) { node.addChildToBack(transform(child)); } return node; } @Override Node processThrowStatement(ThrowStatement statementNode) { return newNode(Token.THROW, transform(statementNode

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> old roots exist (we are parsing a second time), detach each of the // individual file parse trees. if (externsRoot != null) { externsRoot.detachChildren(); } if (jsRoot != null) { jsRoot.detachChildren(); } // Parse main js sources. jsRoot = new Node(Token.BLOCK); jsRoot.setIsSyntheticBlock(true); externsRoot = new Node(Token.BLOCK); externsRoot.setIsSyntheticBlock(true); externAndJsRoot = new Node(Token.BLOCK, externsRoot, jsRoot); externAndJsRoot.setIsSyntheticBlock(true); if (options.tracer.isOn()) { tracker = new PerformanceTracker(jsRoot, options.tracer == TracerMode.ALL); addChangeHandler(tracker.getCodeChangeHandler()); } Tracer tracer = newTracer("parseInputs"); try { // Parse externs sources. for (CompilerInput input : externs) { Node n = input.getAstRoot(this); if (hasErrors()) { return null; } externsRoot.addChildToBack(n); } // Check if the sources need to be re-ordered. if (options.manageClosureDependencies) { for (CompilerInput input : inputs) { input.setCompiler(this); // Forward-declare all the provided types, so that they // are not flagged even if they are dropped from the process. for (String provide : input.getProvides()) { getTypeRegistry().forwardDeclareType(provide); } } try { inputs = (moduleGraph == null ? new JSModuleGraph(modules) : moduleGraph) .manageDependencies( options.manageClosureDependenciesEntryPoints, inputs); } catch (CircularDependencyException e) { report(JSError.make( JSModule.CIRCULAR_DEPENDENCY_ERROR, e.getMessage())); return null; } catch (MissingProvideException e) { report(JSError.make( MISSING_ENTRY_ERROR, e.getMessage())); return null; } } // Check if inputs need to be rebuilt from modules. boolean staleInputs = false; for (CompilerInput input : inputs) { Node n = input.getAstRoot(this); // Inputs can have a null AST during initial parse. if (n == null) { continue; } if (n.getJSDocInfo() != null) { JSDocInfo info = n.getJSDocInfo(); if (info.isExterns()) { // If the input file is explicitly marked as an externs file, then // assume the programmer made a mistake and throw it into // the externs pile anyways. externsRoot.addChildToBack(n); input.setIsExtern(true); input.getModule

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>().remove(input); externs.add(input); staleInputs = true; } else if (info.isNoCompile()) { input.getModule().remove(input); staleInputs = true; } } } if (staleInputs) { fillEmptyModules(modules); rebuildInputsFromModules(); } // Build the AST. for (CompilerInput input : inputs) { Node n = input.getAstRoot(this); if (n == null) { continue; } if (devMode) { runSanityCheck(); if (hasErrors()) { return null; } } if (options.sourceMapOutputPath != null || options.nameReferenceReportPath != null) { // Annotate the nodes in the tree with information from the // input file. This information is used to construct the SourceMap. SourceInformationAnnotator sia = new SourceInformationAnnotator( input.getName(), options.devMode != DevMode.OFF); NodeTraversal.traverse(this, n, sia); } jsRoot.addChildToBack(n); } if (hasErrors()) { return null; } return externAndJsRoot; } finally { stopTracer(tracer, "parseInputs"); } } public Node parse(JSSourceFile file) { initCompilerOptionsIfTesting(); addToDebugLog("Parsing: " + file.getName()); return new JsAst(file).getAstRoot(this); } private int syntheticCodeId = 0; @Override Node parseSyntheticCode(String js) { CompilerInput input = new CompilerInput( JSSourceFile.fromCode(" [synthetic:" + (++syntheticCodeId) + "] ", js)); inputsById.put(input.getInputId(), input); return input.getAstRoot(this); } void initCompilerOptionsIfTesting() { if (options == null) { // initialization for tests that don't initialize the compiler // by the normal mechanisms. initOptions(new CompilerOptions()); } } @Override Node parseSyntheticCode(String fileName, String js) { initCompilerOptionsIfTesting(); return parse(JSSourceFile.fromCode(fileName, js)); } @Override Node parseTestCode(String js) { initCompilerOptionsIfTesting(); CompilerInput input = new CompilerInput( JSSourceFile.fromCode(" [testcode] ", js)); if (inputsById == null) { inputsById = Maps.newHashMap(); } inputsById.put(input.getInputId(), input); return input.getAstRoot(this); } @Override ErrorReporter getDefaultErrorReporter() { return defaultErrorReporter; } //------------------------------------------------------------------------ // Convert back to source code //------------------------------------------------------------------------ /** * Converts the main parse

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>length(); char lastChar = code.charAt(length - 1); char secondLastChar = length >= 2 ? code.charAt(length - 2) : '\0'; boolean hasSemiColon = lastChar == ';' || (lastChar == '\n' && secondLastChar == ';'); if (!hasSemiColon) { cb.append(";"); } } return null; } }); } /** * Generates JavaScript source code for an AST, doesn't generate source * map info. */ @Override String toSource(Node n) { initCompilerOptionsIfTesting(); return toSource(n, null, true); } /** * Generates JavaScript source code for an AST. */ private String toSource(Node n, SourceMap sourceMap, boolean firstOutput) { CodePrinter.Builder builder = new CodePrinter.Builder(n); builder.setPrettyPrint(options.prettyPrint); builder.setLineBreak(options.lineBreak); builder.setSourceMap(sourceMap); builder.setSourceMapDetailLevel(options.sourceMapDetailLevel); builder.setTagAsStrict(firstOutput && options.getLanguageOut() == LanguageMode.ECMASCRIPT5_STRICT); builder.setLineLengthThreshold(options.lineLengthThreshold); Charset charset = options.outputCharset != null ? Charset.forName(options.outputCharset) : null; builder.setOutputCharset(charset); return builder.build(); } /** * Stores a buffer of text to which more can be appended. This is just like a * StringBuilder except that we also track the number of lines. */ public static class CodeBuilder { private final StringBuilder sb = new StringBuilder(); private int lineCount = 0; private int colCount = 0; /** Removes all text, but leaves the line count unchanged. */ void reset() { sb.setLength(0); } /** Appends the given string to the text buffer. */ CodeBuilder append(String str) { sb.append(str); // Adjust the line and column information for the new text. int index = -1; int lastIndex = index; while ((index = str.indexOf('\n', index + 1)) >= 0) { ++lineCount; lastIndex = index; } if (lastIndex == -1) { // No new lines, append the new characters added. colCount += str.length(); } else { colCount = str.length() - (lastIndex + 1); } return this; } /** Returns all text in the text buffer. */ @Override public String toString() { return sb.toString(); } /** Returns the length of the text buffer. */ public int getLength() { return sb.length(); } /** Returns the (zero-

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> { return false; } /** * A null-safe version of JSType#toObjectType. */ public static ObjectType cast(JSType type) { return type == null ? null : type.toObjectType(); } @Override public final boolean isFunctionPrototypeType() { return getOwnerFunction() != null; } /** Gets the owner of this if it's a function prototype. */ public FunctionType getOwnerFunction() { return null; } /** * Gets the interfaces implemented by the ctor associated with this type. * Intended to be overridden by subclasses. */ public Iterable<ObjectType> getCtorImplementedInterfaces() { return ImmutableSet.of(); } /** * Gets the interfaces extended by the interface associated with this type. * Intended to be overriden by subclasses. */ public Iterable<ObjectType> getCtorExtendedInterfaces() { return ImmutableSet.of(); } public static final class Property implements Serializable, StaticSlot<JSType>, StaticReference<JSType> { private static final long serialVersionUID = 1L; /** * Property's name. */ private final String name; /** * Property's type. */ private JSType type; /** * Whether the property's type is inferred. */ private final boolean inferred; /** * The node corresponding to this property, e.g., a GETPROP node that * declares this property. */ private Node propertyNode; /** The JSDocInfo for this property. */ private JSDocInfo docInfo = null; Property(String name, JSType type, boolean inferred, Node propertyNode) { this.name = name; this.type = type; this.inferred = inferred; this.propertyNode = propertyNode; } @Override public String getName() { return name; } @Override public Node getNode() { return propertyNode; } @Override public StaticSourceFile getSourceFile() { return propertyNode == null ? null : propertyNode.getStaticSourceFile(); } @Override public Property getSymbol() { return this; } @Override public Property getDeclaration() { return propertyNode == null ? null : this; } @Override public JSType getType() { return type; } @Override public boolean isTypeInferred() { return inferred; } boolean isFromExterns() { return propertyNode == null ? false : propertyNode.isFromExterns(); } void setType(JSType type) { this.type = type; } @Override public JSDocInfo getJSDocInfo() { return this.docInfo; } void setJSDocInfo(JSDocInfo info) { this.docInfo = info; } public void setNode(Node n) {

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> this.propertyNode = n; } } }

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>/* * Copyright 2006 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Preconditions; import com.google.javascript.rhino.InputId; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; /** * <p>The syntactic scope creator scans the parse tree to create a Scope object * containing all the variable declarations in that scope.</p> * * <p>This implementation is not thread-safe.</p> * */ class SyntacticScopeCreator implements ScopeCreator { private final AbstractCompiler compiler; private Scope scope; private InputId inputId; private final RedeclarationHandler redeclarationHandler; // The arguments variable is special, in that it's declared in every local // scope, but not explicitly declared. private static final String ARGUMENTS = "arguments"; public static final DiagnosticType VAR_MULTIPLY_DECLARED_ERROR = DiagnosticType.error( "JSC_VAR_MULTIPLY_DECLARED_ERROR", "Variable {0} first declared in {1}"); public static final DiagnosticType VAR_ARGUMENTS_SHADOWED_ERROR = DiagnosticType.error( "JSC_VAR_ARGUMENTS_SHADOWED_ERROR", "Shadowing \"arguments\" is not allowed"); /** * Creates a ScopeCreator. */ SyntacticScopeCreator(AbstractCompiler compiler) { this.compiler = compiler; this.redeclarationHandler = new DefaultRedeclarationHandler(); } SyntacticScopeCreator( AbstractCompiler compiler, RedeclarationHandler redeclarationHandler) { this.compiler = compiler; this.redeclarationHandler = redeclarationHandler; } @Override public Scope createScope(Node n, Scope parent) { inputId = null; if (parent == null) { scope = new Scope(n, compiler); } else { scope = new Scope(parent, n); } scanRoot(n, parent); inputId = null; Scope returnedScope = scope; scope = null; return returnedScope; } private void scanRoot(Node n, Scope parent) {

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> if (n.isFunction()) { if (inputId == null) { inputId = NodeUtil.getInputId(n); // TODO(johnlenz): inputId maybe null if the FUNCTION node is detached // from the AST. // Is it meaningful to build a scope for detached FUNCTION node? } final Node fnNameNode = n.getFirstChild(); final Node args = fnNameNode.getNext(); final Node body = args.getNext(); // Bleed the function name into the scope, if it hasn't // been declared in the outer scope. String fnName = fnNameNode.getString(); if (!fnName.isEmpty() && NodeUtil.isFunctionExpression(n)) { declareVar(fnNameNode); } // Args: Declare function variables Preconditions.checkState(args.isParamList()); for (Node a = args.getFirstChild(); a != null; a = a.getNext()) { Preconditions.checkState(a.isName()); declareVar(a); } // Body scanVars(body, n); } else { // It's the global block Preconditions.checkState(scope.getParent() == null); scanVars(n, null); } } /** * Scans and gather variables declarations under a Node */ private void scanVars(Node n, Node parent) { switch (n.getType()) { case Token.VAR: // Declare all variables. e.g. var x = 1, y, z; for (Node child = n.getFirstChild(); child != null;) { Node next = child.getNext(); declareVar(child); child = next; } return; case Token.FUNCTION: if (NodeUtil.isFunctionExpression(n)) { return; } String fnName = n.getFirstChild().getString(); if (fnName.isEmpty()) { // This is invalid, but allow it so the checks can catch it. return; } declareVar(n.getFirstChild()); return; // should not examine function's children case Token.CATCH: Preconditions.checkState(n.getChildCount() == 2); Preconditions.checkState(n.getFirstChild().isName()); // the first child is the catch var and the third child // is the code block final Node var = n.getFirstChild(); final Node block = var.getNext(); declareVar(var); scanVars(block, n); return; // only one child to scan case Token.SCRIPT: inputId = n.getInputId(); Preconditions.checkNotNull(inputId); break; } // Variables can only occur in statement-level nodes, so // we only need to traverse children in a couple special cases. if (NodeUtil.isControlStructure(n) || NodeUtil.isStatementBlock(n)) { for (Node

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> child = n.getFirstChild(); child != null;) { Node next = child.getNext(); scanVars(child, n); child = next; } } } /** * Interface for injectable duplicate handling. */ interface RedeclarationHandler { void onRedeclaration( Scope s, String name, Node n, CompilerInput input); } /** * The default handler for duplicate declarations. */ private class DefaultRedeclarationHandler implements RedeclarationHandler { @Override public void onRedeclaration( Scope s, String name, Node n, CompilerInput input) { Node parent = n.getParent(); // Don't allow multiple variables to be declared at the top level scope if (scope.isGlobal()) { Scope.Var origVar = scope.getVar(name); Node origParent = origVar.getParentNode(); if (origParent.isCatch() && parent.isCatch()) { // Okay, both are 'catch(x)' variables. return; } boolean allowDupe = hasDuplicateDeclarationSuppression(n, origVar); if (!allowDupe) { compiler.report( JSError.make(NodeUtil.getSourceName(n), n, VAR_MULTIPLY_DECLARED_ERROR, name, (origVar.input != null ? origVar.input.getName() : "??"))); } } else if (name.equals(ARGUMENTS) && !NodeUtil.isVarDeclaration(n)) { // Disallow shadowing "arguments" as we can't handle with our current // scope modeling. compiler.report( JSError.make(NodeUtil.getSourceName(n), n, VAR_ARGUMENTS_SHADOWED_ERROR)); } } } /** * Declares a variable. * * @param n The node corresponding to the variable name. */ private void declareVar(Node n) { Preconditions.checkState(n.isName()); CompilerInput input = compiler.getInput(inputId); String name = n.getString(); if (scope.isDeclared(name, false) || (scope.isLocal() && name.equals(ARGUMENTS))) { redeclarationHandler.onRedeclaration( scope, name, n, input); } else { scope.declare(name, n, null, input); } } /** * @param n The name node to check. * @param origVar The associated Var. * @return Whether duplicated declarations warnings should be suppressed * for the given node. */ static boolean hasDuplicateDeclarationSuppression(Node n, Scope.Var origVar) { Preconditions.checkState(n.isName()); Node parent = n.getParent(); Node origParent = origVar.getParentNode(); JSDocInfo info = n.getJSDocInfo(); if (info == null

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> escaped in a * dataflow analysis, it can be reference outside of the code that we are * analyzing. A variable is escaped if any of the following is true: * * <p><ol> * <li>It is defined as the exception name in CATCH clause so it became a * variable local not to our definition of scope.</li> * <li>Exported variables as they can be needed after the script terminates. * </li> * <li>Names of named functions because in javascript, <i>function foo(){}</i> * does not kill <i>foo</i> in the dataflow.</li> */ static void computeEscaped(final Scope jsScope, final Set<Var> escaped, AbstractCompiler compiler) { // TODO(user): Very good place to store this information somewhere. AbstractPostOrderCallback finder = new AbstractPostOrderCallback() { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (jsScope == t.getScope() || !n.isName() || parent.isFunction()) { return; } String name = n.getString(); Var var = t.getScope().getVar(name); if (var != null && var.scope == jsScope) { escaped.add(jsScope.getVar(name)); } } }; NodeTraversal t = new NodeTraversal(compiler, finder); t.traverseAtScope(jsScope); // 1: Remove the exception name in CATCH which technically isn't local to // begin with. for (Iterator<Var> i = jsScope.getVars(); i.hasNext();) { Var var = i.next(); if (var.getParentNode().isCatch() || compiler.getCodingConvention().isExported(var.getName())) { escaped.add(var); } } } }

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>/* * Copyright 2011 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.javascript.jscomp.DiagnosticType; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; /** * {@link CheckDebuggerStatement} checks for the presence of the "debugger" * statement in JavaScript code. It is appropriate to use this statement while * developing JavaScript; however, it is generally undesirable to include it in * production code. * * @author bolinfest@google.com (Michael Bolin) */ class CheckDebuggerStatement extends AbstractPostOrderCallback implements CompilerPass { static final DiagnosticType DEBUGGER_STATEMENT_PRESENT = DiagnosticType.disabled("JSC_DEBUGGER_STATEMENT_PRESENT", "Using the debugger statement can halt your application if the user " + "has a JavaScript debugger running."); private final AbstractCompiler compiler; public CheckDebuggerStatement(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.DEBUGGER) { t.report(n, DEBUGGER_STATEMENT_PRESENT); } } }

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> final DiagnosticType ILLEGAL_NAME = DiagnosticType.error( "JSC_ILLEGAL_NAME", "identifiers ending in '__' cannot be used in Caja"); static final DiagnosticType DUPLICATE_OBJECT_KEY = DiagnosticType.warning( "JSC_DUPLICATE_OBJECT_KEY", "object literals cannot contain duplicate keys in ES5 strict mode"); private final AbstractCompiler compiler; private final boolean noVarCheck; private final boolean noCajaChecks; StrictModeCheck(AbstractCompiler compiler) { this(compiler, false, false); } StrictModeCheck( AbstractCompiler compiler, boolean noVarCheck, boolean noCajaChecks) { this.compiler = compiler; this.noVarCheck = noVarCheck; this.noCajaChecks = noCajaChecks; } @Override public void process(Node externs, Node root) { NodeTraversal.traverseRoots( compiler, Lists.newArrayList(externs, root), this); NodeTraversal.traverse(compiler, root, new NonExternChecks()); } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isName()) { if (!isDeclaration(n)) { checkNameUse(t, n); } } else if (n.isAssign()) { checkAssignment(t, n); } else if (n.getType() == Token.DELPROP) { checkDelete(t, n); } else if (n.isObjectLit()) { checkObjectLiteral(t, n); } else if (n.isLabel()) { checkLabel(t, n); } } /** * Determines if the given name is a declaration, which can be a declaration * of a variable, function, or argument. */ private static boolean isDeclaration(Node n) { switch (n.getParent().getType()) { case Token.VAR: case Token.FUNCTION: case Token.CATCH: return true; case Token.PARAM_LIST: return n.getParent().getParent().isFunction(); default: return false; } } /** Checks that the given name is used legally. */ private void checkNameUse(NodeTraversal t, Node n) { Var v = t.getScope().getVar(n.getString()); if (v == null) { // In particular, this prevents creating a global variable by assigning // to it without a declaration. if (!noVarCheck) { t.report(n, UNKNOWN_VARIABLE, n.getString()); } } if (!noCajaChecks) { if ("eval".equals(n.getString())) { t.report(n, EVAL_USE); } else if (n.getString().endsWith("__")) { t.report(n, ILLEGAL_NAME); } } } /** Checks that

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> an assignment is not to the "arguments" object. */ private void checkAssignment(NodeTraversal t, Node n) { if (n.getFirstChild().isName()) { if ("arguments".equals(n.getFirstChild().getString())) { t.report(n, ARGUMENTS_ASSIGNMENT); } else if ("eval".equals(n.getFirstChild().getString())) { // Note that assignment to eval is already illegal because any use of // that name is illegal. if (noCajaChecks) { t.report(n, EVAL_ASSIGNMENT); } } } } /** Checks that variables, functions, and arguments are not deleted. */ private void checkDelete(NodeTraversal t, Node n) { if (n.getFirstChild().isName()) { Var v = t.getScope().getVar(n.getFirstChild().getString()); if (v != null) { t.report(n, DELETE_VARIABLE); } } } /** Checks that object literal keys are valid. */ private void checkObjectLiteral(NodeTraversal t, Node n) { Set<String> getters = Sets.newHashSet(); Set<String> setters = Sets.newHashSet(); for (Node key = n.getFirstChild(); key != null; key = key.getNext()) { if (!noCajaChecks && key.getString().endsWith("__")) { t.report(key, ILLEGAL_NAME); } if (key.getType() != Token.SETTER_DEF) { // normal property and getter cases if (getters.contains(key.getString())) { t.report(key, DUPLICATE_OBJECT_KEY); } else { getters.add(key.getString()); } } if (key.getType() != Token.GETTER_DEF) { // normal property and setter cases if (setters.contains(key.getString())) { t.report(key, DUPLICATE_OBJECT_KEY); } else { setters.add(key.getString()); } } } } /** Checks that label names are valid. */ private void checkLabel(NodeTraversal t, Node n) { if (n.getFirstChild().getString().endsWith("__")) { if (!noCajaChecks) { t.report(n.getFirstChild(), ILLEGAL_NAME); } } } /** Checks that are performed on non-extern code only. */ private class NonExternChecks extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { if ((n.isName()) && isDeclaration(n)) { checkDeclaration(t, n); } else if (n.isGetProp()) { checkProperty(t, n); } } /** Checks for illegal declarations. */ private void checkDeclaration

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>(NodeTraversal t, Node n) { if ("eval".equals(n.getString())) { t.report(n, EVAL_DECLARATION); } else if ("arguments".equals(n.getString())) { t.report(n, ARGUMENTS_DECLARATION); } else if (n.getString().endsWith("__")) { if (!noCajaChecks) { t.report(n, ILLEGAL_NAME); } } } /** Checks for illegal property accesses. */ private void checkProperty(NodeTraversal t, Node n) { if (n.getLastChild().getString().endsWith("__")) { if (!noCajaChecks) { t.report(n.getLastChild(), ILLEGAL_NAME); } } } } }

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> @Override public void process(Node externs, Node root) { for (TypeMismatch mis : compiler.getTypeValidator().getMismatches()) { addInvalidatingType(mis.typeA); addInvalidatingType(mis.typeB); recordInvalidationError(mis.typeA, mis.src); recordInvalidationError(mis.typeB, mis.src); } StaticScope<T> scope = typeSystem.getRootScope(); NodeTraversal.traverse(compiler, externs, new FindExternProperties()); NodeTraversal.traverse(compiler, root, new FindRenameableProperties()); renameProperties(); } private void recordInvalidationError(JSType t, JSError error) { if (!t.isObject()) { return; } if (t.isUnionType()) { for (JSType alt : t.toMaybeUnionType().getAlternates()) { recordInvalidationError(alt, error); } return; } if (invalidationMap != null) { invalidationMap.put(t, error); } } /** * Invalidates the given type, so that no properties on it will be renamed. */ private void addInvalidatingType(JSType type) { type = type.restrictByNotNullOrUndefined(); if (type.isUnionType()) { for (JSType alt : type.toMaybeUnionType().getAlternates()) { addInvalidatingType(alt); } } else if (type.isEnumElementType()) { addInvalidatingType(type.toMaybeEnumElementType().getPrimitiveType()); } else { typeSystem.addInvalidatingType(type); ObjectType objType = ObjectType.cast(type); if (objType != null && objType.getImplicitPrototype() != null) { typeSystem.addInvalidatingType(objType.getImplicitPrototype()); } } } /** Returns the property for the given name, creating it if necessary. */ protected Property getProperty(String name) { if (!properties.containsKey(name)) { properties.put(name, new Property(name)); } return properties.get(name); } /** Public for testing. */ T getTypeWithProperty(String field, T type) { return typeSystem.getTypeWithProperty(field, type); } /** Tracks the current type system scope while traversing. */ private abstract class AbstractScopingCallback implements ScopedCallback { protected final Stack<StaticScope<T>> scopes = new Stack<StaticScope<T>>(); @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { return true; } @Override public void enterScope(NodeTraversal t) { if (t.inGlobalScope()) { scopes.push(typeSystem.getRootScope()); } else { scopes.push(typeSystem.getFunctionScope

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>(t.getScopeRoot())); } } @Override public void exitScope(NodeTraversal t) { scopes.pop(); } /** Returns the current scope at this point in the file. */ protected StaticScope<T> getScope() { return scopes.peek(); } } /** * Finds all properties defined in the externs file and sets them as * ineligible for renaming from the type on which they are defined. */ private class FindExternProperties extends AbstractScopingCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { // TODO(johnlenz): Support object-literal property definitions. if (n.isGetProp()) { String field = n.getLastChild().getString(); T type = typeSystem.getType(getScope(), n.getFirstChild(), field); Property prop = getProperty(field); if (typeSystem.isInvalidatingType(type)) { prop.invalidate(); } else { prop.addTypeToSkip(type); // If this is a prototype property, then we want to skip assignments // to the instance type as well. These assignments are not usually // seen in the extern code itself, so we must handle them here. if ((type = typeSystem.getInstanceFromPrototype(type)) != null) { prop.getTypes().add(type); prop.typesToSkip.add(type); } } } } } /** * Traverses the tree, building a map from field names to Nodes for all * fields that can be renamed. */ private class FindRenameableProperties extends AbstractScopingCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isGetProp()) { handleGetProp(t, n); } else if (n.isObjectLit()) { handleObjectLit(t, n); } } /** * Processes a GETPROP node. */ private void handleGetProp(NodeTraversal t, Node n) { String name = n.getLastChild().getString(); T type = typeSystem.getType(getScope(), n.getFirstChild(), name); Property prop = getProperty(name); if (!prop.scheduleRenaming(n.getLastChild(), processProperty(t, prop, type, null))) { if (propertiesToErrorFor.containsKey(name)) { String suggestion = ""; if (type instanceof JSType) { JSType jsType = (JSType) type; String qName = n.getFirstChild().getQualifiedName(); if (jsType.isAllType() || jsType.isUnknownType()) { if (n.getFirstChild().isThis()) { suggestion = "The \"this\" object is unknown in the function,"+ "consider using @this"; } else { suggestion = "Consider casting " +

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> qName + " if you know it's type."; } } else { StringBuilder sb = new StringBuilder(); printErrorLocations(sb, jsType); if (sb.length() != 0) { suggestion = "Consider fixing errors for the following types: "; suggestion += sb.toString(); } } } compiler.report(JSError.make( t.getSourceName(), n, propertiesToErrorFor.get(name), Warnings.INVALIDATION, name, (type == null ? "null" : type.toString()), n.toString(), suggestion)); } } } /** * Processes a OBJECTLIT node. */ private void handleObjectLit(NodeTraversal t, Node n) { Node child = n.getFirstChild(); while (child != null) { // Maybe STRING, GET, SET // We should never see a mix of numbers and strings. String name = child.getString(); T type = typeSystem.getType(getScope(), n, name); Property prop = getProperty(name); if (!prop.scheduleRenaming(child, processProperty(t, prop, type, null))) { // TODO(user): It doesn't look like the user can do much in this // case right now. if (propertiesToErrorFor.containsKey(name)) { compiler.report(JSError.make( t.getSourceName(), child, propertiesToErrorFor.get(name), Warnings.INVALIDATION, name, (type == null ? "null" : type.toString()), n.toString(), "")); } } child = child.getNext(); } } private void printErrorLocations(StringBuilder sb, JSType t) { if (!t.isObject() || t.isAllType() || t.isUnionType()) { return; } if (t.isUnionType()) { for (JSType alt : t.toMaybeUnionType().getAlternates()) { printErrorLocations(sb, alt); } return; } for (JSError error : invalidationMap.get(t)) { if(sb.length() != 0) { sb.append(", "); } sb.append(t.toString()); sb.append(" at "); sb.append(error.sourceName); sb.append(":"); sb.append(error.lineNumber); } } /** * Processes a property, adding it to the list of properties to rename. * @return a representative type for the property reference, which will be * the highest type on the prototype chain of the provided type. In the * case of a union type, it will be the highest type on the prototype * chain of one of the members of the union. */ private T processProperty( NodeTraversal t, Property prop, T type, T relatedType) {

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>/* * Copyright 2008 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; /** * Check for usage of 'with'. * */ class ControlStructureCheck implements HotSwapCompilerPass { private final AbstractCompiler compiler; static final DiagnosticType USE_OF_WITH = DiagnosticType.warning( "JSC_USE_OF_WITH", "The use of the 'with' structure should be avoided."); ControlStructureCheck(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { check(root); } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { check(scriptRoot); } /** * Reports errors for any invalid use of control structures. * * @param node Current node to check. */ private void check(Node node) { switch (node.getType()) { case Token.WITH: JSDocInfo info = node.getJSDocInfo(); boolean allowWith = info != null && info.getSuppressions().contains("with"); if (!allowWith) { report(node, USE_OF_WITH); } break; } for (Node bChild = node.getFirstChild(); bChild != null;) { Node next = bChild.getNext(); check(bChild); bChild = next; } } private void report(Node n, DiagnosticType error) { compiler.report(JSError.make(n.getSourceFileName(), n, error)); } }

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>disabled( "JSC_DEPRECATED_PROP_REASON", "Property {0} of type {1} has been deprecated: {2}"); static final DiagnosticType DEPRECATED_CLASS = DiagnosticType.disabled( "JSC_DEPRECATED_CLASS", "Class {0} has been deprecated."); static final DiagnosticType DEPRECATED_CLASS_REASON = DiagnosticType.disabled( "JSC_DEPRECATED_CLASS_REASON", "Class {0} has been deprecated: {1}"); static final DiagnosticType BAD_PRIVATE_GLOBAL_ACCESS = DiagnosticType.disabled( "JSC_BAD_PRIVATE_GLOBAL_ACCESS", "Access to private variable {0} not allowed outside file {1}."); static final DiagnosticType BAD_PRIVATE_PROPERTY_ACCESS = DiagnosticType.disabled( "JSC_BAD_PRIVATE_PROPERTY_ACCESS", "Access to private property {0} of {1} not allowed here."); static final DiagnosticType BAD_PROTECTED_PROPERTY_ACCESS = DiagnosticType.disabled( "JSC_BAD_PROTECTED_PROPERTY_ACCESS", "Access to protected property {0} of {1} not allowed here."); static final DiagnosticType PRIVATE_OVERRIDE = DiagnosticType.disabled( "JSC_PRIVATE_OVERRIDE", "Overriding private property of {0}."); static final DiagnosticType VISIBILITY_MISMATCH = DiagnosticType.disabled( "JSC_VISIBILITY_MISMATCH", "Overriding {0} property of {1} with {2} property."); static final DiagnosticType CONST_PROPERTY_REASSIGNED_VALUE = DiagnosticType.warning( "JSC_CONSTANT_PROPERTY_REASSIGNED_VALUE", "constant property {0} assigned a value more than once"); static final DiagnosticType CONST_PROPERTY_DELETED = DiagnosticType.warning( "JSC_CONSTANT_PROPERTY_DELETED", "constant property {0} cannot be deleted"); private final AbstractCompiler compiler; private final TypeValidator validator; // State about the current traversal. private int deprecatedDepth = 0; private int methodDepth = 0; private JSType currentClass = null; private final Multimap<String, String> initializedConstantProperties; CheckAccessControls(AbstractCompiler compiler) { this.compiler = compiler; this.validator = compiler.getTypeValidator(); this.initializedConstantProperties = HashMultimap.create(); } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { NodeTraversal.traverse(compiler, scriptRoot, this); } @Override public void enterScope(NodeTraversal t) { if (!t.inGlobalScope()) { Node n = t.getScopeRoot(); Node parent = n.getParent(); if (isDeprecatedFunction(

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>n, parent)) { deprecatedDepth++; } if (methodDepth == 0) { currentClass = getClassOfMethod(n, parent); } methodDepth++; } } @Override public void exitScope(NodeTraversal t) { if (!t.inGlobalScope()) { Node n = t.getScopeRoot(); Node parent = n.getParent(); if (isDeprecatedFunction(n, parent)) { deprecatedDepth--; } methodDepth--; if (methodDepth == 0) { currentClass = null; } } } /** * Gets the type of the class that "owns" a method, or null if * we know that its un-owned. */ private JSType getClassOfMethod(Node n, Node parent) { if (parent.isAssign()) { Node lValue = parent.getFirstChild(); if (NodeUtil.isGet(lValue)) { // We have an assignment of the form "a.b = ...". JSType lValueType = lValue.getJSType(); if (lValueType != null && lValueType.isNominalConstructor()) { // If a.b is a constructor, then everything in this function // belongs to the "a.b" type. return (lValueType.toMaybeFunctionType()).getInstanceType(); } else { // If a.b is not a constructor, then treat this as a method // of whatever type is on "a". return normalizeClassType(lValue.getFirstChild().getJSType()); } } else { // We have an assignment of the form "a = ...", so pull the // type off the "a". return normalizeClassType(lValue.getJSType()); } } else if (NodeUtil.isFunctionDeclaration(n) || parent.isName()) { return normalizeClassType(n.getJSType()); } return null; } /** * Normalize the type of a constructor, its instance, and its prototype * all down to the same type (the instance type). */ private JSType normalizeClassType(JSType type) { if (type == null || type.isUnknownType()) { return type; } else if (type.isNominalConstructor()) { return (type.toMaybeFunctionType()).getInstanceType(); } else if (type.isFunctionPrototypeType()) { FunctionType owner = ((ObjectType) type).getOwnerFunction(); if (owner.isConstructor()) { return owner.getInstanceType(); } } return type; } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.NAME: checkNameDeprecation(t, n, parent); checkNameVisibility(t, n

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>, parent); break; case Token.GETPROP: checkPropertyDeprecation(t, n, parent); checkPropertyVisibility(t, n, parent); checkConstantProperty(t, n); break; case Token.NEW: checkConstructorDeprecation(t, n, parent); break; } } /** * Checks the given NEW node to ensure that access restrictions are obeyed. */ private void checkConstructorDeprecation(NodeTraversal t, Node n, Node parent) { JSType type = n.getJSType(); if (type != null) { String deprecationInfo = getTypeDeprecationInfo(type); if (deprecationInfo != null && shouldEmitDeprecationWarning(t, n, parent)) { if (!deprecationInfo.isEmpty()) { compiler.report( t.makeError(n, DEPRECATED_CLASS_REASON, type.toString(), deprecationInfo)); } else { compiler.report( t.makeError(n, DEPRECATED_CLASS, type.toString())); } } } } /** * Checks the given NAME node to ensure that access restrictions are obeyed. */ private void checkNameDeprecation(NodeTraversal t, Node n, Node parent) { // Don't bother checking definitions or constructors. if (parent.isFunction() || parent.isVar() || parent.isNew()) { return; } Scope.Var var = t.getScope().getVar(n.getString()); JSDocInfo docInfo = var == null ? null : var.getJSDocInfo(); if (docInfo != null && docInfo.isDeprecated() && shouldEmitDeprecationWarning(t, n, parent)) { if (docInfo.getDeprecationReason() != null) { compiler.report( t.makeError(n, DEPRECATED_NAME_REASON, n.getString(), docInfo.getDeprecationReason())); } else { compiler.report( t.makeError(n, DEPRECATED_NAME, n.getString())); } } } /** * Checks the given GETPROP node to ensure that access restrictions are * obeyed. */ private void checkPropertyDeprecation(NodeTraversal t, Node n, Node parent) { // Don't bother checking constructors. if (parent.isNew()) { return; } ObjectType objectType = ObjectType.cast(dereference(n.getFirstChild().getJSType())); String propertyName = n.getLastChild().getString(); if (objectType != null) { String deprecationInfo = getPropertyDeprecationInfo(objectType, propertyName); if (deprecationInfo != null && shouldEmitDeprecationWarning(t, n, parent)) { if (!deprecationInfo.isEmpty()) { compiler.report( t.makeError(n, DEPRECATED_PROP_REASON, propertyName, validator.getReadableJS

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>TypeName(n.getFirstChild(), true), deprecationInfo)); } else { compiler.report( t.makeError(n, DEPRECATED_PROP, propertyName, validator.getReadableJSTypeName(n.getFirstChild(), true))); } } } } /** * Determines whether the given name is visible in the current context. * @param t The current traversal. * @param name The name node. */ private void checkNameVisibility(NodeTraversal t, Node name, Node parent) { Var var = t.getScope().getVar(name.getString()); if (var != null) { JSDocInfo docInfo = var.getJSDocInfo(); if (docInfo != null) { // If a name is private, make sure that we're in the same file. Visibility visibility = docInfo.getVisibility(); if (visibility == Visibility.PRIVATE) { StaticSourceFile varSrc = var.getSourceFile(); StaticSourceFile refSrc = name.getStaticSourceFile(); if (varSrc != null && refSrc != null && !varSrc.getName().equals(refSrc.getName())) { if (docInfo.isConstructor() && isValidPrivateConstructorAccess(parent)) { return; } compiler.report( t.makeError(name, BAD_PRIVATE_GLOBAL_ACCESS, name.getString(), varSrc.getName())); } } } } } /** * Determines whether the given property with @const tag got reassigned * @param t The current traversal. * @param getprop The getprop node. */ private void checkConstantProperty(NodeTraversal t, Node getprop) { // Check whether the property is modified Node parent = getprop.getParent(); boolean isDelete = parent.getType() == Token.DELPROP; if (!(NodeUtil.isAssignmentOp(parent) && parent.getFirstChild() == getprop) && (parent.getType() != Token.INC) && (parent.getType() != Token.DEC) && !isDelete) { return; } ObjectType objectType = ObjectType.cast(dereference(getprop.getFirstChild().getJSType())); String propertyName = getprop.getLastChild().getString(); boolean isConstant = isPropertyDeclaredConstant(objectType, propertyName); // Check whether constant properties are reassigned if (isConstant) { if (isDelete) { compiler.report( t.makeError(getprop, CONST_PROPERTY_DELETED, propertyName)); return; } ObjectType oType = objectType; while (oType != null) { if (oType.hasReferenceName()) { if (initializedConstantProperties.containsEntry( oType.getReferenceName(), propertyName)) { compiler.report( t.makeError(getprop, CONST_PROPERTY_REASSIGNED_VALUE, propertyName)); break; }

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>ridingVisibility != Visibility.INHERITED && overridingVisibility != visibility) { compiler.report( t.makeError(getprop, VISIBILITY_MISMATCH, visibility.name(), objectType.toString(), overridingVisibility.name())); } } else { if (sameInput) { // private access is always allowed in the same file. return; } else if (visibility == Visibility.PRIVATE && (currentClass == null || ownerType.differsFrom(currentClass))) { if (docInfo.isConstructor() && isValidPrivateConstructorAccess(parent)) { return; } // private access is not allowed outside the file from a different // enclosing class. compiler.report( t.makeError(getprop, BAD_PRIVATE_PROPERTY_ACCESS, propertyName, validator.getReadableJSTypeName( getprop.getFirstChild(), true))); } else if (visibility == Visibility.PROTECTED) { // There are 3 types of legal accesses of a protected property: // 1) Accesses in the same file // 2) Overriding the property in a subclass // 3) Accessing the property from inside a subclass // The first two have already been checked for. if (currentClass == null || !currentClass.isSubtype(ownerType)) { compiler.report( t.makeError(getprop, BAD_PROTECTED_PROPERTY_ACCESS, propertyName, validator.getReadableJSTypeName( getprop.getFirstChild(), true))); } } } } } /** * Whether the given access of a private constructor is legal. * * For example, * new PrivateCtor_(); // not legal * PrivateCtor_.newInstance(); // legal * x instanceof PrivateCtor_ // legal * * This is a weird special case, because our visibility system is inherited * from Java, and JavaScript has no distinction between classes and * constructors like Java does. * * We may want to revisit this if we decide to make the restrictions tighter. */ private static boolean isValidPrivateConstructorAccess(Node parent) { return parent.getType() != Token.NEW; } /** * Determines whether a deprecation warning should be emitted. * @param t The current traversal. * @param n The node which we are checking. * @param parent The parent of the node which we are checking. */ private boolean shouldEmitDeprecationWarning( NodeTraversal t, Node n, Node parent) { // In the global scope, there are only two kinds of accesses that should // be flagged for warnings: // 1) Calls of deprecated functions and methods. // 2) Instantiations of deprecated classes. // For now, we just let everything else by. if (t.inGlobalScope()) { if (!((parent.isCall() && parent.getFirstChild() == n) ||

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> n.isNew())) { return false; } } // We can always assign to a deprecated property, to keep it up to date. if (n.isGetProp() && n == parent.getFirstChild() && NodeUtil.isAssignmentOp(parent)) { return false; } return !canAccessDeprecatedTypes(t); } /** * Returns whether it's currently ok to access deprecated names and * properties. * * There are 3 exceptions when we're allowed to use a deprecated * type or property: * 1) When we're in a deprecated function. * 2) When we're in a deprecated class. * 3) When we're in a static method of a deprecated class. */ private boolean canAccessDeprecatedTypes(NodeTraversal t) { Node scopeRoot = t.getScopeRoot(); Node scopeRootParent = scopeRoot.getParent(); return // Case #1 (deprecatedDepth > 0) || // Case #2 (getTypeDeprecationInfo(t.getScope().getTypeOfThis()) != null) || // Case #3 (scopeRootParent != null && scopeRootParent.isAssign() && getTypeDeprecationInfo( getClassOfMethod(scopeRoot, scopeRootParent)) != null); } /** * Returns whether this is a function node annotated as deprecated. */ private static boolean isDeprecatedFunction(Node n, Node parent) { if (n.isFunction()) { JSType type = n.getJSType(); if (type != null) { return getTypeDeprecationInfo(type) != null; } } return false; } /** * Returns the deprecation reason for the type if it is marked * as being deprecated. Returns empty string if the type is deprecated * but no reason was given. Returns null if the type is not deprecated. */ private static String getTypeDeprecationInfo(JSType type) { if (type == null) { return null; } JSDocInfo info = type.getJSDocInfo(); if (info != null && info.isDeprecated()) { if (info.getDeprecationReason() != null) { return info.getDeprecationReason(); } return ""; } ObjectType objType = ObjectType.cast(type); if (objType != null) { ObjectType implicitProto = objType.getImplicitPrototype(); if (implicitProto != null) { return getTypeDeprecationInfo(implicitProto); } } return null; } /** * Returns if a property is declared constant. */ private static boolean isPropertyDeclaredConstant( ObjectType objectType, String prop) { for (; // Only objects with reference names can have constant properties. objectType != null && objectType.hasReferenceName(); objectType = objectType.getImplicitPrototype()) { JSDocInfo docInfo = object

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> Types for each optional parameter. The builder will make them * undefineable. * @return False if this is called after var args are added. */ public boolean addOptionalParams(JSType ...types) { if (hasVarArgs()) { return false; } for (JSType type : types) { newParameter(registry.createOptionalType(type)).setOptionalArg(true); } return true; } /** * Add variable arguments to the end of the parameter list. * @return False if this is called after var args are added. */ public boolean addVarArgs(JSType type) { if (hasVarArgs()) { return false; } // There are two types of variable argument functions: // 1) Programmer-defined var args // 2) Native bottom types that can accept any argument. // For the first one, "undefined" is a valid value for all arguments. // For the second, we do not want to cast it up to undefined. if (!type.isEmptyType()) { type = registry.createOptionalType(type); } newParameter(type).setVarArgs(true); return true; } /** * Copies the parameter specification from the given node. */ public Node newParameterFromNode(Node n) { Node newParam = newParameter(n.getJSType()); newParam.setVarArgs(n.isVarArgs()); newParam.setOptionalArg(n.isOptionalArg()); return newParam; } // Add a parameter to the list with the given type. private Node newParameter(JSType type) { Node paramNode = Node.newString(Token.NAME, ""); paramNode.setJSType(type); root.addChildToBack(paramNode); return paramNode; } public Node build() { return root; } private boolean hasOptionalOrVarArgs() { Node lastChild = root.getLastChild(); return lastChild != null && (lastChild.isOptionalArg() || lastChild.isVarArgs()); } public boolean hasVarArgs() { Node lastChild = root.getLastChild(); return lastChild != null && lastChild.isVarArgs(); } }

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> as a marker * for a variable argument list function. A VarArgs parameter must be the * last parameter in a function declaration. * * @param parameter The parameter's node. * @return {@code true} if the parameter should be treated as a variable * length parameter. */ public boolean isVarArgsParameter(Node parameter); /** * Checks whether a global variable or function name should be treated as * exported, or externally referenceable. * * @param name A global variable or function name. * @param local {@code true} if the name is a local variable. * @return {@code true} if the name should be considered exported. */ public boolean isExported(String name, boolean local); /** * Should be isExported(name, true) || isExported(name, false); */ public boolean isExported(String name); /** * Checks whether a name should be considered private. Private global * variables and functions can only be referenced within the source file in * which they are declared. Private properties and methods should only be * accessed by the class that defines them. * * @param name The name of a global variable or function, or a method or * property. * @return {@code true} if the name should be considered private. */ public boolean isPrivate(String name); /** * Checks if the given method defines a subclass relationship, * and if it does, returns information on that relationship. By default, * always returns null. Meant to be overridden by subclasses. * * @param callNode A CALL node. */ public SubclassRelationship getClassesDefinedByCall(Node callNode); /** * Returns true if passed a string referring to the superclass. The string * will usually be from the string node at the right of a GETPROP, e.g. * this.superClass_. */ public boolean isSuperClassReference(String propertyName); /** * Convenience method for determining provided dependencies amongst different * js scripts. */ public String extractClassNameIfProvide(Node node, Node parent); /** * Convenience method for determining required dependencies amongst different * js scripts. */ public String extractClassNameIfRequire(Node node, Node parent); /** * Function name used when exporting properties. * Signature: fn(object, publicName, symbol). * @return function name. */ public String getExportPropertyFunction(); /** * Function name used when exporting symbols. * Signature: fn(publicPath, object). * @return function name. */ public String getExportSymbolFunction(); /** * Checks if the given CALL node is forward-declaring any types, * and returns the name of the types if it is. */ public List<String> identifyTypeDeclarationCall(Node n); /** * In many JS libraries, the function that produces inheritance also

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> * adds properties to the superclass and/or subclass. */ public void applySubclassRelationship(FunctionType parentCtor, FunctionType childCtor, SubclassType type); /** * Function name for abstract methods. An abstract method can be assigned to * an interface method instead of an function expression in order to avoid * linter warnings produced by assigning a function without a return value * where a return value is expected. * @return function name. */ public String getAbstractMethodName(); /** * Checks if the given method defines a singleton getter, and if it does, * returns the name of the class with the singleton getter. By default, always * returns null. Meant to be overridden by subclasses. * * @param callNode A CALL node. */ public String getSingletonGetterClassName(Node callNode); /** * In many JS libraries, the function that adds a singleton getter to a class * adds properties to the class. */ public void applySingletonGetter(FunctionType functionType, FunctionType getterType, ObjectType objectType); public DelegateRelationship getDelegateRelationship(Node callNode); /** * In many JS libraries, the function that creates a delegate relationship * also adds properties to the delegator and delegate base. */ public void applyDelegateRelationship( ObjectType delegateSuperclass, ObjectType delegateBase, ObjectType delegator, FunctionType delegateProxy, FunctionType findDelegate); /** * @return the name of the delegate superclass. */ public String getDelegateSuperclassName(); /** * Checks for function calls that set the calling conventions on delegate * methods. */ public void checkForCallingConventionDefiningCalls( Node n, Map<String, String> delegateCallingConventions); /** * Defines the delegate proxy prototype properties. Their types depend on * properties of the delegate base methods. * * @param delegateProxyPrototypes List of delegate proxy prototypes. */ public void defineDelegateProxyPrototypeProperties( JSTypeRegistry registry, Scope scope, List<ObjectType> delegateProxyPrototypes, Map<String, String> delegateCallingConventions); /** * Gets the name of the global object. */ public String getGlobalObject(); /** * A Bind instance or null. */ public Bind describeFunctionBind(Node n); public static class Bind { // The target of the bind action final Node target; // The node representing the "this" value, maybe null final Node thisValue; // The head of a Node list representing the parameters final Node parameters; public Bind(Node target, Node thisValue, Node parameters) { this.target = target; this.thisValue = thisValue; this.parameters = parameters; } } /** * Whether this CALL function is testing for the existence of a property. */ public boolean isPropertyTestFunction(Node call); /** * Checks if the given method performs a object literal cast

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>Lineno, endCharno, true); } return typeNode; } /** * Looks for a parameter type expression at the current token and if found, * returns it. Note that this method consumes input. * * @param token The current token. * @param lineno The line of the type expression. * @param startCharno The starting character position of the type expression. * @param matchingLC Whether the type expression starts with a "{". * @param onlyParseSimpleNames If true, only simple type names are parsed * (via a call to parseTypeNameAnnotation instead of * parseTypeExpressionAnnotation). * @return The type expression found or null if none. */ private Node parseAndRecordTypeNode(JsDocToken token, int lineno, int startCharno, boolean matchingLC, boolean onlyParseSimpleNames) { Node typeNode = null; if (onlyParseSimpleNames) { typeNode = parseTypeNameAnnotation(token); } else { typeNode = parseTypeExpressionAnnotation(token); } if (typeNode != null && !matchingLC) { typeNode.putBooleanProp(Node.BRACELESS_TYPE, true); } if (typeNode != null) { int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); jsdocBuilder.markTypeNode( typeNode, lineno, startCharno, endLineno, endCharno, matchingLC); } return typeNode; } /** * Converts a JSDoc token to its string representation. */ private String toString(JsDocToken token) { switch (token) { case ANNOTATION: return "@" + stream.getString(); case BANG: return "!"; case COMMA: return ","; case COLON: return ":"; case GT: return ">"; case LB: return "["; case LC: return "{"; case LP: return "("; case LT: return ".<"; case QMARK: return "?"; case PIPE: return "|"; case RB: return "]"; case RC: return "}"; case RP: return ")"; case STAR: return "*"; case ELLIPSIS: return "..."; case EQUALS: return "="; case STRING: return stream.getString(); default: throw new IllegalStateException(token.toString()); } } /** * Constructs a new {@code JSTypeExpression}. * @param n A node. May be null. */ private JSTypeExpression createJSTypeExpression(Node n) { return n == null ? null : new JSTypeExpression(n, getSourceName()); } /** * Tuple for returning both the string extracted and the * new token following a call to any of the extract*Block

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>(fieldType); skipEOLs(); if (!match(JsDocToken.COMMA)) { break; } // Move to the comma token. next(); // Move to the token passed the comma. skipEOLs(); token = next(); } while (true); return fieldTypeList; } /** * FieldType := FieldName | FieldName ':' TypeExpression */ private Node parseFieldType(JsDocToken token) { Node fieldName = parseFieldName(token); if (fieldName == null) { return null; } skipEOLs(); if (!match(JsDocToken.COLON)) { return fieldName; } // Move to the colon. next(); // Move to the token after the colon and parse // the type expression. skipEOLs(); Node typeExpression = parseTypeExpression(next()); if (typeExpression == null) { return null; } Node fieldType = newNode(Token.COLON); fieldType.addChildToBack(fieldName); fieldType.addChildToBack(typeExpression); return fieldType; } /** * FieldName := NameExpression | StringLiteral | NumberLiteral | * ReservedIdentifier */ private Node parseFieldName(JsDocToken token) { switch (token) { case STRING: String string = stream.getString(); return newStringNode(string); default: return null; } } private Node wrapNode(int type, Node n) { return n == null ? null : new Node(type, n, stream.getLineno(), stream.getCharno()).clonePropsFrom(templateNode); } private Node newNode(int type) { return new Node(type, stream.getLineno(), stream.getCharno()).clonePropsFrom(templateNode); } private Node newStringNode(String s) { return newStringNode(s, stream.getLineno(), stream.getCharno()); } private Node newStringNode(String s, int lineno, int charno) { Node n = Node.newString(s, lineno, charno).clonePropsFrom(templateNode); n.setLength(s.length()); return n; } // This is similar to IRFactory.createTemplateNode to share common props // e.g., source-name, between all nodes. private Node createTemplateNode() { // The Node type choice is arbitrary. Node templateNode = new Node(Token.SCRIPT); templateNode.setStaticSourceFile( this.associatedNode != null ? this.associatedNode.getStaticSourceFile() : null); return templateNode; } private Node reportTypeSyntaxWarning(String warning) { parser.addTypeWarning(warning, stream.getLineno(), stream.getCharno()); return null; } private Node reportGenericTypeSyntaxWarning() { return reportTypeSyntaxWarning("msg.js

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> list of neighboring nodes. */ public abstract List<GraphNode<N, E>> getNeighborNodes(N value); public abstract Iterator<GraphNode<N, E>> getNeighborNodesIterator(N value); /** * Retrieves an edge from the graph. * * @param n1 Node one. * @param n2 Node two. * @return The list of edges between those two values in the graph. */ public abstract List<GraphEdge<N, E>> getEdges(N n1, N n2); /** * Retrieves any edge from the graph. * * @param n1 Node one. * @param n2 Node two. * @return The first edges between those two values in the graph. null if * there are none. */ public abstract GraphEdge<N, E> getFirstEdge(N n1, N n2); /** * Checks whether the node exists in the graph ({@link #createNode(Object)} * has been called with that value). * * @param n Node. * @return <code>true</code> if it exist. */ public final boolean hasNode(N n) { return getNode(n) != null; } /** * Checks whether two nodes in the graph are connected. * * @param n1 Node 1. * @param n2 Node 2. * @return <code>true</code> if the two nodes are connected. */ public abstract boolean isConnected(N n1, N n2); /** * Checks whether two nodes in the graph are connected by the given * edge type. * * @param n1 Node 1. * @param e The edge type. * @param n2 Node 2. */ public abstract boolean isConnected(N n1, E e, N n2); /** * Gets the node of the specified type, or throws an * IllegalArgumentException. */ @SuppressWarnings("unchecked") <T extends GraphNode<N, E>> T getNodeOrFail(N val) { T node = (T) getNode(val); if (node == null) { throw new IllegalArgumentException(val + " does not exist in graph"); } return node; } @Override public final void clearNodeAnnotations() { for (GraphNode<N, E> n : getNodes()) { n.setAnnotation(null); } } /** Makes each edge's annotation null. */ public final void clearEdgeAnnotations() { for (GraphEdge<N, E> e : getEdges()) { e.setAnnotation(null); } } /** * Pushes nodes' annotation values. Restored with * {@link #popNodeAnnotations()}. Nodes' annotation values are cleared. */ public final void pushNodeAnnotations() { if (nodeAnnotationStack == null

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>) { nodeAnnotationStack = Lists.newLinkedList(); } pushAnnotations(nodeAnnotationStack, getNodes()); } /** * Restores nodes' annotation values to state before last * {@link #pushNodeAnnotations()}. */ public final void popNodeAnnotations() { Preconditions.checkNotNull(nodeAnnotationStack, "Popping node annotations without pushing."); popAnnotations(nodeAnnotationStack); } /** * Pushes edges' annotation values. Restored with * {@link #popEdgeAnnotations()}. Edges' annotation values are cleared. */ public final void pushEdgeAnnotations() { if (edgeAnnotationStack == null) { edgeAnnotationStack = Lists.newLinkedList(); } pushAnnotations(edgeAnnotationStack, getEdges()); } /** * Restores edges' annotation values to state before last * {@link #pushEdgeAnnotations()}. */ public final void popEdgeAnnotations() { Preconditions.checkNotNull(edgeAnnotationStack, "Popping edge annotations without pushing."); popAnnotations(edgeAnnotationStack); } /** * A generic edge. * * @param <N> Value type that the graph node stores. * @param <E> Value type that the graph edge stores. */ public interface GraphEdge<N, E> extends Annotatable { /** * Retrieves the edge's value. * * @return The value. */ E getValue(); GraphNode<N, E> getNodeA(); GraphNode<N, E> getNodeB(); } /** * A simple implementation of SubGraph that calculates adjacency by iterating * over a node's neighbors. */ class SimpleSubGraph<N, E> implements SubGraph<N, E> { private Graph<N, E> graph; private List<GraphNode<N, E>> nodes = Lists.newArrayList(); SimpleSubGraph(Graph<N, E> graph) { this.graph = graph; } @Override public boolean isIndependentOf(N value) { GraphNode<N, E> node = graph.getNode(value); for (GraphNode<N, E> n : nodes) { if (graph.getNeighborNodes(n.getValue()).contains(node)) { return false; } } return true; } @Override public void addNode(N value) { nodes.add(graph.getNodeOrFail(value)); } } /** * Pushes a new list on stack and stores nodes annotations in the new list. * Clears objects' annotations as well. */ private static void pushAnnotations( Deque<GraphAnnotationState> stack, Collection<? extends Annotatable> haveAnnotations) { stack.push(new GraphAnnotationState(haveAnnotations.size())); for (Annotatable h : haveAnnotations) { stack.peek().add(new AnnotationState(h, h.getAnnotation())); h.set

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>/* * Copyright 2008 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Predicate; import com.google.javascript.jscomp.ControlFlowGraph.Branch; import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; import com.google.javascript.jscomp.graph.GraphNode; import com.google.javascript.jscomp.graph.GraphReachability; import com.google.javascript.jscomp.graph.GraphReachability.EdgeTuple; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.TernaryValue; /** * Use {@link ControlFlowGraph} and {@link GraphReachability} to inform user * about unreachable code. * */ class CheckUnreachableCode implements ScopedCallback { static final DiagnosticType UNREACHABLE_CODE = DiagnosticType.error( "JSC_UNREACHABLE_CODE", "unreachable code"); private final AbstractCompiler compiler; private final CheckLevel level; CheckUnreachableCode(AbstractCompiler compiler, CheckLevel level) { this.compiler = compiler; this.level = level; } @Override public void enterScope(NodeTraversal t) { initScope(t.getControlFlowGraph()); } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { GraphNode<Node, Branch> gNode = t.getControlFlowGraph().getNode(n); if (gNode != null && gNode.getAnnotation() != GraphReachability.REACHABLE) { // Only report error when there are some line number informations. // There are synthetic nodes with no line number informations, nodes // introduce by other passes (although not likely since this pass should // be executed early) or some rhino bug. if (n.getLineno() != -1 && // Allow spurious semi-colons and spurious breaks. n.getType() != Token.EMPTY && n.getType() != Token.BREAK) { compiler.report(t.makeError(n, level, UNREACHABLE_CODE)); // From now on

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>, we are going to assume the user fixed the error and not // give more warning related to code section reachable from this node. new GraphReachability<Node, ControlFlowGraph.Branch>( t.getControlFlowGraph()).recompute(n); // Saves time by not traversing children. return false; } } return true; } private void initScope(ControlFlowGraph<Node> controlFlowGraph) { new GraphReachability<Node, ControlFlowGraph.Branch>( controlFlowGraph, new ReachablePredicate()).compute( controlFlowGraph.getEntry().getValue()); } @Override public void exitScope(NodeTraversal t) { } @Override public void visit(NodeTraversal t, Node n, Node parent) { } private final class ReachablePredicate implements Predicate<EdgeTuple<Node, ControlFlowGraph.Branch>> { @Override public boolean apply(EdgeTuple<Node, Branch> input) { Branch branch = input.edge; if (!branch.isConditional()) { return true; } Node predecessor = input.sourceNode; Node condition = NodeUtil.getConditionExpression(predecessor); // TODO(user): Handle more complicated expression like true == true, // etc.... if (condition != null) { TernaryValue val = NodeUtil.getImpureBooleanValue(condition); if (val != TernaryValue.UNKNOWN) { return val.toBoolean(true) == (branch == Branch.ON_TRUE); } } return true; } } }

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> { Preconditions.checkNotNull(errorManager, "Expected setErrorManager to be called first"); try { regenerateDependencyInfoIfNecessary(); return Collections.<String>unmodifiableSet(provides); } catch (IOException e) { errorManager.report(CheckLevel.ERROR, JSError.make(AbstractCompiler.READ_ERROR, getName())); return ImmutableList.<String>of(); } } /** * Regenerates the provides/requires if we need to do so. */ private void regenerateDependencyInfoIfNecessary() throws IOException { // If the code is NOT a JsAst, then it was not originally JS code. // Look at the Ast for dependency info. if (!(ast instanceof JsAst)) { Preconditions.checkNotNull(compiler, "Expected setCompiler to be called first"); DepsFinder finder = new DepsFinder(); Node root = getAstRoot(compiler); if (root == null) { return; } finder.visitTree(getAstRoot(compiler)); // TODO(nicksantos|user): This caching behavior is a bit // odd, and only works if you assume the exact call flow that // clients are currently using. In that flow, they call // getProvides(), then remove the goog.provide calls from the // AST, and then call getProvides() again. // // This won't work for any other call flow, or any sort of incremental // compilation scheme. The API needs to be fixed so callers aren't // doing weird things like this, and then we should get rid of the // multiple-scan strategy. provides.addAll(finder.provides); requires.addAll(finder.requires); } else { // Otherwise, look at the source code. if (!generatedDependencyInfoFromSource) { // Note: it's ok to use getName() instead of // getPathRelativeToClosureBase() here because we're not using // this to generate deps files. (We're only using it for // symbol dependencies.) DependencyInfo info = (new JsFileParser(errorManager)).parseFile( getName(), getName(), getCode()); provides.addAll(info.getProvides()); requires.addAll(info.getRequires()); generatedDependencyInfoFromSource = true; } } } private static class DepsFinder { private final List<String> provides = Lists.newArrayList(); private final List<String> requires = Lists.newArrayList(); private final CodingConvention codingConvention = new ClosureCodingConvention(); void visitTree(Node n) { visitSubtree(n, null); } void visitSubtree(Node n, Node parent) { if (n.isCall()) { String require = codingConvention.extractClassNameIfRequire(n, parent); if (require != null) { requires.add(require); } String provide = codingConvention.extractClassNameIfProvide(n, parent

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>); if (provide != null) { provides.add(provide); } return; } else if (parent != null && parent.getType() != Token.EXPR_RESULT && parent.getType() != Token.SCRIPT) { return; } for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { visitSubtree(child, n); } } } /** * Gets the source line for the indicated line number. * * @param lineNumber the line number, 1 being the first line of the file. * @return The line indicated. Does not include the newline at the end * of the file. Returns {@code null} if it does not exist, * or if there was an IO exception. */ public String getLine(int lineNumber) { return getSourceFile().getLine(lineNumber); } /** * Get a region around the indicated line number. The exact definition of a * region is implementation specific, but it must contain the line indicated * by the line number. A region must not start or end by a carriage return. * * @param lineNumber the line number, 1 being the first line of the file. * @return The line indicated. Returns {@code null} if it does not exist, * or if there was an IO exception. */ public Region getRegion(int lineNumber) { return getSourceFile().getRegion(lineNumber); } public String getCode() throws IOException { return getSourceFile().getCode(); } /** Returns the module to which the input belongs. */ public JSModule getModule() { return module; } /** Sets the module to which the input belongs. */ public void setModule(JSModule module) { // An input may only belong to one module. Preconditions.checkArgument( module == null || this.module == null || this.module == module); this.module = module; } public boolean isExtern() { if (ast == null || ast.getSourceFile() == null) { return false; } return ast.getSourceFile().isExtern(); } void setIsExtern(boolean isExtern) { if (ast == null || ast.getSourceFile() == null) { return; } ast.getSourceFile().setIsExtern(isExtern); } public int getLineOffset(int lineno) { return ast.getSourceFile().getLineOffset(lineno); } /** @return The number of lines in this input. */ public int getNumLines() { return ast.getSourceFile().getNumLines(); } @Override public String toString() { return getName(); } }

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> * * @param sourceName The source file name * @param lineno Line number with source file, or -1 if unknown * @param charno Column number within line, or -1 for whole line. * @param type The DiagnosticType * @param arguments Arguments to be incorporated into the message */ public static JSError make(String sourceName, int lineno, int charno, CheckLevel level, DiagnosticType type, String... arguments) { return new JSError( sourceName, null, lineno, charno, type, level, arguments); } /** * Creates a JSError from a file and Node position. * * @param sourceName The source file name * @param n Determines the line and char position within the source file name * @param type The DiagnosticType * @param arguments Arguments to be incorporated into the message */ public static JSError make(String sourceName, Node n, DiagnosticType type, String... arguments) { return new JSError(sourceName, n, type, arguments); } /** * Creates a JSError from a file and Node position. * * @param sourceName The source file name * @param n Determines the line and char position within the source file name * @param type The DiagnosticType * @param arguments Arguments to be incorporated into the message */ public static JSError make(String sourceName, Node n, CheckLevel level, DiagnosticType type, String... arguments) { return new JSError(sourceName, n, n.getLineno(), n.getCharno(), type, level, arguments); } // // JSError constructors // /** * Creates a JSError at a CheckLevel for a source file location. * Private to avoid any entanglement with code outside of the compiler. */ private JSError( String sourceName, @Nullable Node node, int lineno, int charno, DiagnosticType type, CheckLevel level, String... arguments) { this.type = type; this.node = node; this.description = type.format.format(arguments); this.lineNumber = lineno; this.charno = charno; this.sourceName = sourceName; this.level = level == null ? type.level : level; } /** * Creates a JSError for a source file location. Private to avoid * any entanglement with code outside of the compiler. */ private JSError(String sourceName, @Nullable Node node, DiagnosticType type, String... arguments) { this(sourceName, node, (node != null) ? node.getLineno() : -1, (node != null) ? node.getCharno() : -1, type, null, arguments); } public DiagnosticType getType() { return type; } /** * Format a message at the

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>code> JavaScript cast function. * Note: unlike getBooleanValue this function does not return UNKNOWN * for expressions with side-effects. */ static TernaryValue getImpureBooleanValue(Node n) { switch (n.getType()) { case Token.ASSIGN: case Token.COMMA: // For ASSIGN and COMMA the value is the value of the RHS. return getImpureBooleanValue(n.getLastChild()); case Token.NOT: TernaryValue value = getImpureBooleanValue(n.getLastChild()); return value.not(); case Token.AND: { TernaryValue lhs = getImpureBooleanValue(n.getFirstChild()); TernaryValue rhs = getImpureBooleanValue(n.getLastChild()); return lhs.and(rhs); } case Token.OR: { TernaryValue lhs = getImpureBooleanValue(n.getFirstChild()); TernaryValue rhs = getImpureBooleanValue(n.getLastChild()); return lhs.or(rhs); } case Token.HOOK: { TernaryValue trueValue = getImpureBooleanValue( n.getFirstChild().getNext()); TernaryValue falseValue = getImpureBooleanValue(n.getLastChild()); if (trueValue.equals(falseValue)) { return trueValue; } else { return TernaryValue.UNKNOWN; } } case Token.ARRAYLIT: case Token.OBJECTLIT: // ignoring side-effects return TernaryValue.TRUE; case Token.VOID: return TernaryValue.FALSE; default: return getPureBooleanValue(n); } } /** * Gets the boolean value of a node that represents a literal. This method * effectively emulates the <code>Boolean()</code> JavaScript cast function * except it return UNKNOWN for known values with side-effects, use * getExpressionBooleanValue if you don't care about side-effects. */ static TernaryValue getPureBooleanValue(Node n) { switch (n.getType()) { case Token.STRING: return TernaryValue.forBoolean(n.getString().length() > 0); case Token.NUMBER: return TernaryValue.forBoolean(n.getDouble() != 0); case Token.NOT: return getPureBooleanValue(n.getLastChild()).not(); case Token.NULL: case Token.FALSE: return TernaryValue.FALSE; case Token.VOID: if (!mayHaveSideEffects(n.getFirstChild())) { return TernaryValue.FALSE; } break; case Token.NAME: String name = n.getString(); if ("undefined".equals(name) || "NaN".equals(name)) { // We assume here that programs don't change the value of the keyword // undefined to something other than the value undefined. return TernaryValue.

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>FALSE; } else if ("Infinity".equals(name)) { return TernaryValue.TRUE; } break; case Token.TRUE: case Token.REGEXP: return TernaryValue.TRUE; case Token.ARRAYLIT: case Token.OBJECTLIT: if (!mayHaveSideEffects(n)) { return TernaryValue.TRUE; } break; } return TernaryValue.UNKNOWN; } /** * Gets the value of a node as a String, or null if it cannot be converted. * When it returns a non-null String, this method effectively emulates the * <code>String()</code> JavaScript cast function. */ static String getStringValue(Node n) { // TODO(user): regex literals as well. switch (n.getType()) { case Token.STRING: return n.getString(); case Token.NAME: String name = n.getString(); if ("undefined".equals(name) || "Infinity".equals(name) || "NaN".equals(name)) { return name; } break; case Token.NUMBER: return getStringValue(n.getDouble()); case Token.FALSE: case Token.TRUE: case Token.NULL: return Node.tokenToName(n.getType()); case Token.VOID: return "undefined"; case Token.NOT: TernaryValue child = getPureBooleanValue(n.getFirstChild()); if (child != TernaryValue.UNKNOWN) { return child.toBoolean(true) ? "false" : "true"; // reversed. } break; case Token.ARRAYLIT: return arrayToString(n); case Token.OBJECTLIT: return "[object Object]"; } return null; } static String getStringValue(double value) { long longValue = (long) value; // Return "1" instead of "1.0" if (longValue == value) { return Long.toString(longValue); } else { return Double.toString(value); } } /** * When converting arrays to string using Array.prototype.toString or * Array.prototype.join, the rules for conversion to String are different * than converting each element individually. Specifically, "null" and * "undefined" are converted to an empty string. * @param n A node that is a member of an Array. * @return The string representation. */ static String getArrayElementStringValue(Node n) { return (NodeUtil.isNullOrUndefined(n) || n.isEmpty()) ? "" : getStringValue(n); } static String arrayToString(Node literal) { Node first = literal.getFirstChild(); StringBuilder result = new StringBuilder(); int nextSlot = 0; int nextSkipSlot = 0; for (Node n = first; n != null; n = n

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>.getNext()) { String childValue = getArrayElementStringValue(n); if (childValue == null) { return null; } if (n != first) { result.append(','); } result.append(childValue); nextSlot++; } return result.toString(); } /** * Gets the value of a node as a Number, or null if it cannot be converted. * When it returns a non-null Double, this method effectively emulates the * <code>Number()</code> JavaScript cast function. */ static Double getNumberValue(Node n) { switch (n.getType()) { case Token.TRUE: return 1.0; case Token.FALSE: case Token.NULL: return 0.0; case Token.NUMBER: return n.getDouble(); case Token.VOID: if (mayHaveSideEffects(n.getFirstChild())) { return null; } else { return Double.NaN; } case Token.NAME: // Check for known constants String name = n.getString(); if (name.equals("undefined")) { return Double.NaN; } if (name.equals("NaN")) { return Double.NaN; } if (name.equals("Infinity")) { return Double.POSITIVE_INFINITY; } return null; case Token.NEG: if (n.getChildCount() == 1 && n.getFirstChild().isName() && n.getFirstChild().getString().equals("Infinity")) { return Double.NEGATIVE_INFINITY; } return null; case Token.NOT: TernaryValue child = getPureBooleanValue(n.getFirstChild()); if (child != TernaryValue.UNKNOWN) { return child.toBoolean(true) ? 0.0 : 1.0; // reversed. } break; case Token.STRING: return getStringNumberValue(n.getString()); case Token.ARRAYLIT: case Token.OBJECTLIT: String value = getStringValue(n); return value != null ? getStringNumberValue(value) : null; } return null; } static Double getStringNumberValue(String rawJsString) { if (rawJsString.contains("\u000b")) { // vertical tab is not always whitespace return null; } String s = trimJsWhiteSpace(rawJsString); // return ScriptRuntime.toNumber(s); if (s.length() == 0) { return 0.0; } if (s.length() > 2 && s.charAt(0) == '0' && (s.charAt(1) == 'x' || s.charAt(1) == 'X')) { // Attempt to convert hex numbers. try { return Double.valueOf(Integer.parseInt(s.substring(2), 1

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> cases with named function expressions, the second name is * returned (the variable of qualified name). * * @param n a node whose type is {@link Token#FUNCTION} * @return the function's name, or {@code null} if it has no name */ static String getFunctionName(Node n) { Node parent = n.getParent(); String name = n.getFirstChild().getString(); switch (parent.getType()) { case Token.NAME: // var name = function() ... // var name2 = function name1() ... return parent.getString(); case Token.ASSIGN: // qualified.name = function() ... // qualified.name2 = function name1() ... return parent.getFirstChild().getQualifiedName(); default: // function name() ... return name != null && name.length() != 0 ? name : null; } } /** * Gets the function's name. This method recognizes the forms: * <ul> * <li>{@code &#123;'name': function() ...&#125;}</li> * <li>{@code &#123;name: function() ...&#125;}</li> * <li>{@code function name() ...}</li> * <li>{@code var name = function() ...}</li> * <li>{@code qualified.name = function() ...}</li> * <li>{@code var name2 = function name1() ...}</li> * <li>{@code qualified.name2 = function name1() ...}</li> * </ul> * * @param n a node whose type is {@link Token#FUNCTION} * @return the function's name, or {@code null} if it has no name */ public static String getNearestFunctionName(Node n) { String name = getFunctionName(n); if (name != null) { return name; } // Check for the form { 'x' : function() { } } Node parent = n.getParent(); switch (parent.getType()) { case Token.SETTER_DEF: case Token.GETTER_DEF: case Token.STRING: // Return the name of the literal's key. return parent.getString(); case Token.NUMBER: return getStringValue(parent); } return null; } /** * Returns true if this is an immutable value. */ static boolean isImmutableValue(Node n) { switch (n.getType()) { case Token.STRING: case Token.NUMBER: case Token.NULL: case Token.TRUE: case Token.FALSE: return true; case Token.NOT: return isImmutableValue(n.getFirstChild()); case Token.VOID: case Token.NEG: return isImmutableValue(n.getFirstChild()); case Token.NAME: String name = n.getString();

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> // We assume here that programs don't change the value of the keyword // undefined to something other than the value undefined. return "undefined".equals(name) || "Infinity".equals(name) || "NaN".equals(name); } return false; } /** * Returns true if the operator on this node is symmetric */ public static boolean isSymmetricOperation(Node n) { switch (n.getType()) { case Token.EQ: // equal case Token.NE: // not equal case Token.SHEQ: // exactly equal case Token.SHNE: // exactly not equal case Token.MUL: // multiply, unlike add it only works on numbers // or results NaN if any of the operators is not a number return true; } return false; } /** * Returns true if the operator on this node is relational. * the returned set does not include the equalities. */ public static boolean isRelationalOperation(Node n) { switch (n.getType()) { case Token.GT: // equal case Token.GE: // not equal case Token.LT: // exactly equal case Token.LE: // exactly not equal return true; } return false; } /** * Returns the inverse of an operator if it is invertible. * ex. '>' ==> '<' */ public static int getInverseOperator(int type) { switch (type) { case Token.GT: return Token.LT; case Token.LT: return Token.GT; case Token.GE: return Token.LE; case Token.LE: return Token.GE; } return Token.ERROR; } /** * Returns true if this is a literal value. We define a literal value * as any node that evaluates to the same thing regardless of when or * where it is evaluated. So /xyz/ and [3, 5] are literals, but * the name a is not. * * Function literals do not meet this definition, because they * lexically capture variables. For example, if you have * <code> * function() { return a; } * </code> * If it is evaluated in a different scope, then it * captures a different variable. Even if the function did not read * any captured vairables directly, it would still fail this definition, * because it affects the lifecycle of variables in the enclosing scope. * * However, a function literal with respect to a particular scope is * a literal. * * @param includeFunctions If true, all function expressions will be * treated as literals. */ static boolean isLiteralValue(Node n, boolean includeFunctions) { switch (n.getType()) { case Token.ARRAYLIT: for (Node child = n.getFirstChild(); child != null; child = child.getNext

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>()) { if ((!child.isEmpty()) && !isLiteralValue(child, includeFunctions)) { return false; } } return true; case Token.REGEXP: // Return true only if all children are const. for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { if (!isLiteralValue(child, includeFunctions)) { return false; } } return true; case Token.OBJECTLIT: // Return true only if all values are const. for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { if (!isLiteralValue(child.getFirstChild(), includeFunctions)) { return false; } } return true; case Token.FUNCTION: return includeFunctions && !NodeUtil.isFunctionDeclaration(n); default: return isImmutableValue(n); } } /** * Determines whether the given value may be assigned to a define. * * @param val The value being assigned. * @param defines The list of names of existing defines. */ static boolean isValidDefineValue(Node val, Set<String> defines) { switch (val.getType()) { case Token.STRING: case Token.NUMBER: case Token.TRUE: case Token.FALSE: return true; // Binary operators are only valid if both children are valid. case Token.ADD: case Token.BITAND: case Token.BITNOT: case Token.BITOR: case Token.BITXOR: case Token.DIV: case Token.EQ: case Token.GE: case Token.GT: case Token.LE: case Token.LSH: case Token.LT: case Token.MOD: case Token.MUL: case Token.NE: case Token.RSH: case Token.SHEQ: case Token.SHNE: case Token.SUB: case Token.URSH: return isValidDefineValue(val.getFirstChild(), defines) && isValidDefineValue(val.getLastChild(), defines); // Uniary operators are valid if the child is valid. case Token.NOT: case Token.NEG: case Token.POS: return isValidDefineValue(val.getFirstChild(), defines); // Names are valid if and only if they are defines themselves. case Token.NAME: case Token.GETPROP: if (val.isQualifiedName()) { return defines.contains(val.getQualifiedName()); } } return false; } /** * Returns whether this a BLOCK node with no children. * * @param block The node. */ static boolean isEmptyBlock(Node block) { if (!block.isBlock()) { return false; } for (Node n = block.getFirstChild(); n != null; n = n.getNext()) {

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> if (!n.isEmpty()) { return false; } } return true; } static boolean isSimpleOperator(Node n) { return isSimpleOperatorType(n.getType()); } /** * A "simple" operator is one whose children are expressions, * has no direct side-effects (unlike '+='), and has no * conditional aspects (unlike '||'). */ static boolean isSimpleOperatorType(int type) { switch (type) { case Token.ADD: case Token.BITAND: case Token.BITNOT: case Token.BITOR: case Token.BITXOR: case Token.COMMA: case Token.DIV: case Token.EQ: case Token.GE: case Token.GETELEM: case Token.GETPROP: case Token.GT: case Token.INSTANCEOF: case Token.LE: case Token.LSH: case Token.LT: case Token.MOD: case Token.MUL: case Token.NE: case Token.NOT: case Token.RSH: case Token.SHEQ: case Token.SHNE: case Token.SUB: case Token.TYPEOF: case Token.VOID: case Token.POS: case Token.NEG: case Token.URSH: return true; default: return false; } } /** * Creates an EXPR_RESULT. * * @param child The expression itself. * @return Newly created EXPR node with the child as subexpression. */ public static Node newExpr(Node child) { Node expr = new Node(Token.EXPR_RESULT, child) .copyInformationFrom(child); return expr; } /** * Returns true if the node may create new mutable state, or change existing * state. * * @see <a href="http://www.xkcd.org/326/">XKCD Cartoon</a> */ static boolean mayEffectMutableState(Node n) { return mayEffectMutableState(n, null); } static boolean mayEffectMutableState(Node n, AbstractCompiler compiler) { return checkForStateChangeHelper(n, true, compiler); } /** * Returns true if the node which may have side effects when executed. */ static boolean mayHaveSideEffects(Node n) { return mayHaveSideEffects(n, null); } static boolean mayHaveSideEffects(Node n, AbstractCompiler compiler) { return checkForStateChangeHelper(n, false, compiler); } /** * Returns true if some node in n's subtree changes application state. * If {@code checkForNewObjects} is true, we assume that newly created * mutable objects (like object literals) change state. Otherwise, we assume * that they have no side effects. */ private static boolean

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> checkForStateChangeHelper( Node n, boolean checkForNewObjects, AbstractCompiler compiler) { // Rather than id which ops may have side effects, id the ones // that we know to be safe switch (n.getType()) { // other side-effect free statements and expressions case Token.AND: case Token.BLOCK: case Token.EXPR_RESULT: case Token.HOOK: case Token.IF: case Token.IN: case Token.PARAM_LIST: case Token.NUMBER: case Token.OR: case Token.THIS: case Token.TRUE: case Token.FALSE: case Token.NULL: case Token.STRING: case Token.SWITCH: case Token.TRY: case Token.EMPTY: break; // Throws are by definition side effects case Token.THROW: return true; case Token.OBJECTLIT: if (checkForNewObjects) { return true; } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (checkForStateChangeHelper( c.getFirstChild(), checkForNewObjects, compiler)) { return true; } } return false; case Token.ARRAYLIT: case Token.REGEXP: if (checkForNewObjects) { return true; } break; case Token.VAR: // empty var statement (no declaration) case Token.NAME: // variable by itself if (n.getFirstChild() != null) { return true; } break; case Token.FUNCTION: // Function expressions don't have side-effects, but function // declarations change the namespace. Either way, we don't need to // check the children, since they aren't executed at declaration time. return checkForNewObjects || !isFunctionExpression(n); case Token.NEW: if (checkForNewObjects) { return true; } if (!constructorCallHasSideEffects(n)) { // loop below will see if the constructor parameters have // side-effects break; } return true; case Token.CALL: // calls to functions that have no side effects have the no // side effect property set. if (!functionCallHasSideEffects(n, compiler)) { // loop below will see if the function parameters have // side-effects break; } return true; default: if (isSimpleOperatorType(n.getType())) { break; } if (isAssignmentOp(n)) { Node assignTarget = n.getFirstChild(); if (assignTarget.isName()) { return true; } // Assignments will have side effects if // a) The RHS has side effects, or // b) The LHS has side effects, or // c) A name on the LHS will exist beyond the life of this statement. if (

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>checkForStateChangeHelper( n.getFirstChild(), checkForNewObjects, compiler) || checkForStateChangeHelper( n.getLastChild(), checkForNewObjects, compiler)) { return true; } if (isGet(assignTarget)) { // If the object being assigned to is a local object, don't // consider this a side-effect as it can't be referenced // elsewhere. Don't do this recursively as the property might // be an alias of another object, unlike a literal below. Node current = assignTarget.getFirstChild(); if (evaluatesToLocalValue(current)) { return false; } // A literal value as defined by "isLiteralValue" is guaranteed // not to be an alias, or any components which are aliases of // other objects. // If the root object is a literal don't consider this a // side-effect. while (isGet(current)) { current = current.getFirstChild(); } return !isLiteralValue(current, true); } else { // TODO(johnlenz): remove this code and make this an exception. This // is here only for legacy reasons, the AST is not valid but // preserve existing behavior. return !isLiteralValue(assignTarget, true); } } return true; } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (checkForStateChangeHelper(c, checkForNewObjects, compiler)) { return true; } } return false; } /** * Do calls to this constructor have side effects? * * @param callNode - construtor call node */ static boolean constructorCallHasSideEffects(Node callNode) { return constructorCallHasSideEffects(callNode, null); } static boolean constructorCallHasSideEffects( Node callNode, AbstractCompiler compiler) { if (!callNode.isNew()) { throw new IllegalStateException( "Expected NEW node, got " + Token.name(callNode.getType())); } if (callNode.isNoSideEffectsCall()) { return false; } Node nameNode = callNode.getFirstChild(); if (nameNode.isName() && CONSTRUCTORS_WITHOUT_SIDE_EFFECTS.contains(nameNode.getString())) { return false; } return true; } // A list of built-in object creation or primitive type cast functions that // can also be called as constructors but lack side-effects. // TODO(johnlenz): consider adding an extern annotation for this. private static final Set<String> BUILTIN_FUNCTIONS_WITHOUT_SIDEEFFECTS = ImmutableSet.of( "Object", "Array", "String", "Number", "Boolean", "RegExp", "Error"); private static final Set<String> OBJECT_METHODS_WITHOUT_SIDEEFFECT

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> boolean callHasLocalResult(Node n) { Preconditions.checkState(n.isCall()); return (n.getSideEffectFlags() & Node.FLAG_LOCAL_RESULTS) > 0; } /** * @return Whether the new has a local result. */ static boolean newHasLocalResult(Node n) { Preconditions.checkState(n.isNew()); return n.isOnlyModifiesThisCall(); } /** * Returns true if the current node's type implies side effects. * * This is a non-recursive version of the may have side effects * check; used to check wherever the current node's type is one of * the reason's why a subtree has side effects. */ static boolean nodeTypeMayHaveSideEffects(Node n) { return nodeTypeMayHaveSideEffects(n, null); } static boolean nodeTypeMayHaveSideEffects(Node n, AbstractCompiler compiler) { if (isAssignmentOp(n)) { return true; } switch(n.getType()) { case Token.DELPROP: case Token.DEC: case Token.INC: case Token.THROW: return true; case Token.CALL: return NodeUtil.functionCallHasSideEffects(n, compiler); case Token.NEW: return NodeUtil.constructorCallHasSideEffects(n, compiler); case Token.NAME: // A variable definition. return n.hasChildren(); default: return false; } } /** * @return Whether the tree can be affected by side-effects or * has side-effects. */ static boolean canBeSideEffected(Node n) { Set<String> emptySet = Collections.emptySet(); return canBeSideEffected(n, emptySet); } /** * @param knownConstants A set of names known to be constant value at * node 'n' (such as locals that are last written before n can execute). * @return Whether the tree can be affected by side-effects or * has side-effects. */ static boolean canBeSideEffected(Node n, Set<String> knownConstants) { switch (n.getType()) { case Token.CALL: case Token.NEW: // Function calls or constructor can reference changed values. // TODO(johnlenz): Add some mechanism for determining that functions // are unaffected by side effects. return true; case Token.NAME: // Non-constant names values may have been changed. return !isConstantName(n) && !knownConstants.contains(n.getString()); // Properties on constant NAMEs can still be side-effected. case Token.GETPROP: case Token.GETELEM: return true; case Token.FUNCTION: // Function expression are not changed by side-effects, // and function declarations are not part of expressions.

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> Preconditions.checkState(isFunctionExpression(n)); return false; } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (canBeSideEffected(c, knownConstants)) { return true; } } return false; } /* * 0 comma , * 1 assignment = += -= *= /= %= <<= >>= >>>= &= ^= |= * 2 conditional ?: * 3 logical-or || * 4 logical-and && * 5 bitwise-or | * 6 bitwise-xor ^ * 7 bitwise-and & * 8 equality == != * 9 relational < <= > >= * 10 bitwise shift << >> >>> * 11 addition/subtraction + - * 12 multiply/divide * / % * 13 negation/increment ! ~ - ++ -- * 14 call, member () [] . */ static int precedence(int type) { switch (type) { case Token.COMMA: return 0; case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITAND: case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.ASSIGN_URSH: case Token.ASSIGN_ADD: case Token.ASSIGN_SUB: case Token.ASSIGN_MUL: case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: case Token.ASSIGN: return 1; case Token.HOOK: return 2; // ?: operator case Token.OR: return 3; case Token.AND: return 4; case Token.BITOR: return 5; case Token.BITXOR: return 6; case Token.BITAND: return 7; case Token.EQ: case Token.NE: case Token.SHEQ: case Token.SHNE: return 8; case Token.LT: case Token.GT: case Token.LE: case Token.GE: case Token.INSTANCEOF: case Token.IN: return 9; case Token.LSH: case Token.RSH: case Token.URSH: return 10; case Token.SUB: case Token.ADD: return 11; case Token.MUL: case Token.MOD: case Token.DIV: return 12; case Token.INC: case Token.DEC: case Token.NEW: case Token.DELPROP: case Token.TYPEOF: case Token.VOID: case Token.NOT: case Token.BITNOT: case Token.POS: case Token.NEG: return 13; case Token.CALL: case Token.

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>GETELEM: case Token.GETPROP: // Data values case Token.ARRAYLIT: case Token.EMPTY: // TODO(johnlenz): remove this. case Token.FALSE: case Token.FUNCTION: case Token.NAME: case Token.NULL: case Token.NUMBER: case Token.OBJECTLIT: case Token.REGEXP: case Token.STRING: case Token.THIS: case Token.TRUE: return 15; default: throw new Error("Unknown precedence for " + Node.tokenToName(type) + " (type " + type + ")"); } } /** * Apply the supplied predicate against the potential * all possible result of the expression. */ static boolean valueCheck(Node n, Predicate<Node> p) { switch (n.getType()) { case Token.ASSIGN: case Token.COMMA: return valueCheck(n.getLastChild(), p); case Token.AND: case Token.OR: return valueCheck(n.getFirstChild(), p) && valueCheck(n.getLastChild(), p); case Token.HOOK: return valueCheck(n.getFirstChild().getNext(), p) && valueCheck(n.getLastChild(), p); default: return p.apply(n); } } static class NumbericResultPredicate implements Predicate<Node> { @Override public boolean apply(Node n) { return isNumericResultHelper(n); } } static final NumbericResultPredicate NUMBERIC_RESULT_PREDICATE = new NumbericResultPredicate(); /** * Returns true if the result of node evaluation is always a number */ static boolean isNumericResult(Node n) { return valueCheck(n, NUMBERIC_RESULT_PREDICATE); } static boolean isNumericResultHelper(Node n) { switch (n.getType()) { case Token.ADD: return !mayBeString(n.getFirstChild()) && !mayBeString(n.getLastChild()); case Token.BITNOT: case Token.BITOR: case Token.BITXOR: case Token.BITAND: case Token.LSH: case Token.RSH: case Token.URSH: case Token.SUB: case Token.MUL: case Token.MOD: case Token.DIV: case Token.INC: case Token.DEC: case Token.POS: case Token.NEG: case Token.NUMBER: return true; case Token.NAME: String name = n.getString(); if (name.equals("NaN")) { return true; } if (name.equals("Infinity")) { return true; } return false; default: return false; } } static class BooleanResultPredicate implements Predicate<Node> { @Override public boolean apply(Node n

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>) { return isBooleanResultHelper(n); } } static final BooleanResultPredicate BOOLEAN_RESULT_PREDICATE = new BooleanResultPredicate(); /** * @return Whether the result of node evaluation is always a boolean */ static boolean isBooleanResult(Node n) { return valueCheck(n, BOOLEAN_RESULT_PREDICATE); } static boolean isBooleanResultHelper(Node n) { switch (n.getType()) { // Primitives case Token.TRUE: case Token.FALSE: // Comparisons case Token.EQ: case Token.NE: case Token.SHEQ: case Token.SHNE: case Token.LT: case Token.GT: case Token.LE: case Token.GE: // Queryies case Token.IN: case Token.INSTANCEOF: // Inversion case Token.NOT: // delete operator returns a boolean. case Token.DELPROP: return true; default: return false; } } static boolean isUndefined(Node n) { switch (n.getType()) { case Token.VOID: return true; case Token.NAME: return n.getString().equals("undefined"); } return false; } static boolean isNullOrUndefined(Node n) { return n.isNull() || isUndefined(n); } static class MayBeStringResultPredicate implements Predicate<Node> { @Override public boolean apply(Node n) { return mayBeStringHelper(n); } } static final MayBeStringResultPredicate MAY_BE_STRING_PREDICATE = new MayBeStringResultPredicate(); /** * @returns Whether the results is possibly a string. */ static boolean mayBeString(Node n) { return mayBeString(n, true); } static boolean mayBeString(Node n, boolean recurse) { if (recurse) { return valueCheck(n, MAY_BE_STRING_PREDICATE); } else { return mayBeStringHelper(n); } } static boolean mayBeStringHelper(Node n) { return !isNumericResult(n) && !isBooleanResult(n) && !isUndefined(n) && !n.isNull(); } /** * Returns true if the operator is associative. * e.g. (a * b) * c = a * (b * c) * Note: "+" is not associative because it is also the concatenation * for strings. e.g. "a" + (1 + 2) is not "a" + 1 + 2 */ static boolean isAssociative(int type) { switch (type) { case Token.MUL: case Token.AND: case Token.OR: case Token.BITOR: case Token.BITXOR:

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> case Token.BITAND: return true; default: return false; } } /** * Returns true if the operator is commutative. * e.g. (a * b) * c = c * (b * a) * Note 1: "+" is not commutative because it is also the concatenation * for strings. e.g. "a" + (1 + 2) is not "a" + 1 + 2 * Note 2: only operations on literals and pure functions are commutative. */ static boolean isCommutative(int type) { switch (type) { case Token.MUL: case Token.BITOR: case Token.BITXOR: case Token.BITAND: return true; default: return false; } } static boolean isAssignmentOp(Node n) { switch (n.getType()){ case Token.ASSIGN: case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITAND: case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.ASSIGN_URSH: case Token.ASSIGN_ADD: case Token.ASSIGN_SUB: case Token.ASSIGN_MUL: case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: return true; } return false; } static int getOpFromAssignmentOp(Node n) { switch (n.getType()){ case Token.ASSIGN_BITOR: return Token.BITOR; case Token.ASSIGN_BITXOR: return Token.BITXOR; case Token.ASSIGN_BITAND: return Token.BITAND; case Token.ASSIGN_LSH: return Token.LSH; case Token.ASSIGN_RSH: return Token.RSH; case Token.ASSIGN_URSH: return Token.URSH; case Token.ASSIGN_ADD: return Token.ADD; case Token.ASSIGN_SUB: return Token.SUB; case Token.ASSIGN_MUL: return Token.MUL; case Token.ASSIGN_DIV: return Token.DIV; case Token.ASSIGN_MOD: return Token.MOD; } throw new IllegalArgumentException("Not an assiment op:" + n); } static boolean isExpressionNode(Node n) { return n.isExprResult(); } /** * Determines if the given node contains a function statement or function * expression. */ static boolean containsFunction(Node n) { return containsType(n, Token.FUNCTION); } /** * Returns true if the shallow scope contains references to 'this' keyword */ static boolean referencesThis(Node n) { Node start = (n.isFunction()) ? n.getLastChild() : n; return containsType(start, Token.

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>THIS, MATCH_NOT_FUNCTION); } /** * Is this a GETPROP or GETELEM node? */ static boolean isGet(Node n) { return n.isGetProp() || n.isGetElem(); } /** * Is this node the name of a variable being declared? * * @param n The node * @return True if {@code n} is NAME and {@code parent} is VAR */ static boolean isVarDeclaration(Node n) { // There is no need to verify that parent != null because a NAME node // always has a parent in a valid parse tree. return n.isName() && n.getParent().isVar(); } /** * For an assignment or variable declaration get the assigned value. * @return The value node representing the new value. */ static Node getAssignedValue(Node n) { Preconditions.checkState(n.isName()); Node parent = n.getParent(); if (parent.isVar()) { return n.getFirstChild(); } else if (parent.isAssign() && parent.getFirstChild() == n) { return n.getNext(); } else { return null; } } /** * Is this node an assignment expression statement? * * @param n The node * @return True if {@code n} is EXPR_RESULT and {@code n}'s * first child is ASSIGN */ static boolean isExprAssign(Node n) { return n.isExprResult() && n.getFirstChild().isAssign(); } /** * Is this node a call expression statement? * * @param n The node * @return True if {@code n} is EXPR_RESULT and {@code n}'s * first child is CALL */ static boolean isExprCall(Node n) { return n.isExprResult() && n.getFirstChild().isCall(); } /** * @return Whether the node represents a FOR-IN loop. */ static boolean isForIn(Node n) { return n.isFor() && n.getChildCount() == 3; } /** * Determines whether the given node is a FOR, DO, or WHILE node. */ static boolean isLoopStructure(Node n) { switch (n.getType()) { case Token.FOR: case Token.DO: case Token.WHILE: return true; default: return false; } } /** * @param n The node to inspect. * @return If the node, is a FOR, WHILE, or DO, it returns the node for * the code BLOCK, null otherwise. */ static Node getLoopCodeBlock(Node n) { switch (n.getType()) { case Token.FOR: case Token.WHILE: return n.getLastChild();

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> case Token.DO: return n.getFirstChild(); default: return null; } } /** * @return Whether the specified node has a loop parent that * is within the current scope. */ static boolean isWithinLoop(Node n) { for (Node parent : n.getAncestors()) { if (NodeUtil.isLoopStructure(parent)) { return true; } if (parent.isFunction()) { break; } } return false; } /** * Determines whether the given node is a FOR, DO, WHILE, WITH, or IF node. */ static boolean isControlStructure(Node n) { switch (n.getType()) { case Token.FOR: case Token.DO: case Token.WHILE: case Token.WITH: case Token.IF: case Token.LABEL: case Token.TRY: case Token.CATCH: case Token.SWITCH: case Token.CASE: case Token.DEFAULT_CASE: return true; default: return false; } } /** * Determines whether the given node is code node for FOR, DO, * WHILE, WITH, or IF node. */ static boolean isControlStructureCodeBlock(Node parent, Node n) { switch (parent.getType()) { case Token.FOR: case Token.WHILE: case Token.LABEL: case Token.WITH: return parent.getLastChild() == n; case Token.DO: return parent.getFirstChild() == n; case Token.IF: return parent.getFirstChild() != n; case Token.TRY: return parent.getFirstChild() == n || parent.getLastChild() == n; case Token.CATCH: return parent.getLastChild() == n; case Token.SWITCH: case Token.CASE: return parent.getFirstChild() != n; case Token.DEFAULT_CASE: return true; default: Preconditions.checkState(isControlStructure(parent)); return false; } } /** * Gets the condition of an ON_TRUE / ON_FALSE CFG edge. * @param n a node with an outgoing conditional CFG edge * @return the condition node or null if the condition is not obviously a node */ static Node getConditionExpression(Node n) { switch (n.getType()) { case Token.IF: case Token.WHILE: return n.getFirstChild(); case Token.DO: return n.getLastChild(); case Token.FOR: switch (n.getChildCount()) { case 3: return null; case 4: return n.getFirstChild().getNext(); } throw new IllegalArgumentException("malformed 'for' statement " + n); case Token.CASE: return null; } throw new IllegalArgumentException(n + " does not have a

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> condition."); } /** * @return Whether the node is of a type that contain other statements. */ static boolean isStatementBlock(Node n) { return n.isScript() || n.isBlock(); } /** * @return Whether the node is used as a statement. */ static boolean isStatement(Node n) { return isStatementParent(n.getParent()); } static boolean isStatementParent(Node parent) { // It is not possible to determine definitely if a node is a statement // or not if it is not part of the AST. A FUNCTION node can be // either part of an expression or a statement. Preconditions.checkState(parent != null); switch (parent.getType()) { case Token.SCRIPT: case Token.BLOCK: case Token.LABEL: return true; default: return false; } } /** Whether the node is part of a switch statement. */ static boolean isSwitchCase(Node n) { return n.isCase() || n.isDefaultCase(); } /** * @return Whether the name is a reference to a variable, function or * function parameter (not a label or a empty function expression name). */ static boolean isReferenceName(Node n) { return n.isName() && !n.getString().isEmpty(); } /** Whether the child node is the FINALLY block of a try. */ static boolean isTryFinallyNode(Node parent, Node child) { return parent.isTry() && parent.getChildCount() == 3 && child == parent.getLastChild(); } /** Whether the node is a CATCH container BLOCK. */ static boolean isTryCatchNodeContainer(Node n) { Node parent = n.getParent(); return parent.isTry() && parent.getFirstChild().getNext() == n; } /** Safely remove children while maintaining a valid node structure. */ static void removeChild(Node parent, Node node) { if (isTryFinallyNode(parent, node)) { if (NodeUtil.hasCatchHandler(getCatchBlock(parent))) { // A finally can only be removed if there is a catch. parent.removeChild(node); } else { // Otherwise only its children can be removed. node.detachChildren(); } } else if (node.isCatch()) { // The CATCH can can only be removed if there is a finally clause. Node tryNode = node.getParent().getParent(); Preconditions.checkState(NodeUtil.hasFinally(tryNode)); node.detachFromParent(); } else if (isTryCatchNodeContainer(node)) { // The container node itself can't be removed, but the contained CATCH // can if there is a 'finally' clause Node tryNode = node.getParent(); Preconditions.checkState(NodeUtil.hasFinally(tryNode)); node.detachChildren

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> static Node getFunctionBody(Node fn) { Preconditions.checkArgument(fn.isFunction()); return fn.getLastChild(); } /** * Is this node or any of its children a CALL? */ static boolean containsCall(Node n) { return containsType(n, Token.CALL); } /** * Is this node a function declaration? A function declaration is a function * that has a name that is added to the current scope (i.e. a function that * is not part of a expression; see {@link #isFunctionExpression}). */ static boolean isFunctionDeclaration(Node n) { return n.isFunction() && isStatement(n); } /** * Is this node a hoisted function declaration? A function declaration in the * scope root is hoisted to the top of the scope. * See {@link #isFunctionDeclaration}). */ static boolean isHoistedFunctionDeclaration(Node n) { return isFunctionDeclaration(n) && (n.getParent().isScript() || n.getParent().getParent().isFunction()); } /** * Is a FUNCTION node an function expression? An function expression is one * that has either no name or a name that is not added to the current scope. * * <p>Some examples of function expressions: * <pre> * (function () {}) * (function f() {})() * [ function f() {} ] * var f = function f() {}; * for (function f() {};;) {} * </pre> * * <p>Some examples of functions that are <em>not</em> expressions: * <pre> * function f() {} * if (x); else function f() {} * for (;;) { function f() {} } * </pre> * * @param n A node * @return Whether n is an function used within an expression. */ static boolean isFunctionExpression(Node n) { return n.isFunction() && !isStatement(n); } /** * Determines if a node is a function expression that has an empty body. * * @param node a node * @return whether the given node is a function expression that is empty */ static boolean isEmptyFunctionExpression(Node node) { return isFunctionExpression(node) && isEmptyBlock(node.getLastChild()); } /** * Determines if a function takes a variable number of arguments by * looking for references to the "arguments" var_args object. */ static boolean isVarArgsFunction(Node function) { Preconditions.checkArgument(function.isFunction()); return isNameReferenced( function.getLastChild(), "arguments", MATCH_NOT_FUNCTION); } /** * @return Whether node is a call to methodName. * a.f(...) * a['f'](...) */ static boolean isObjectCallMethod(Node callNode,

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> String methodName) { if (callNode.isCall()) { Node functionIndentifyingExpression = callNode.getFirstChild(); if (isGet(functionIndentifyingExpression)) { Node last = functionIndentifyingExpression.getLastChild(); if (last != null && last.isString()) { String propName = last.getString(); return (propName.equals(methodName)); } } } return false; } /** * @return Whether the callNode represents an expression in the form of: * x.call(...) * x['call'](...) */ static boolean isFunctionObjectCall(Node callNode) { return isObjectCallMethod(callNode, "call"); } /** * @return Whether the callNode represents an expression in the form of: * x.apply(...) * x['apply'](...) */ static boolean isFunctionObjectApply(Node callNode) { return isObjectCallMethod(callNode, "apply"); } /** * @return Whether the callNode represents an expression in the form of: * x.apply(...) * x['apply'](...) * or * x.call(...) * x['call'](...) */ static boolean isFunctionObjectCallOrApply(Node callNode) { return isFunctionObjectCall(callNode) || isFunctionObjectApply(callNode); } /** * @return Whether the callNode represents an expression in the form of: * x.call(...) * x['call'](...) * where x is a NAME node. */ static boolean isSimpleFunctionObjectCall(Node callNode) { if (isFunctionObjectCall(callNode)) { if (callNode.getFirstChild().getFirstChild().isName()) { return true; } } return false; } /** * Determines whether this node is strictly on the left hand side of an assign * or var initialization. Notably, this does not include all L-values, only * statements where the node is used only as an L-value. * * @param n The node * @param parent Parent of the node * @return True if n is the left hand of an assign */ static boolean isVarOrSimpleAssignLhs(Node n, Node parent) { return (parent.isAssign() && parent.getFirstChild() == n) || parent.isVar(); } /** * Determines whether this node is used as an L-value. Notice that sometimes * names are used as both L-values and R-values. * * We treat "var x;" as a pseudo-L-value, which kind of makes sense if you * treat it as "assignment to 'undefined' at the top of the scope". But if * we're honest with ourselves, it doesn't make sense, and we only do this * because it makes

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> sense to treat this as synactically similar to * "var x = 0;". * * @param n The node * @return True if n is an L-value. */ static boolean isLValue(Node n) { Preconditions.checkArgument(n.isName() || n.isGetProp() || n.isGetElem()); Node parent = n.getParent(); return (NodeUtil.isAssignmentOp(parent) && parent.getFirstChild() == n) || (NodeUtil.isForIn(parent) && parent.getFirstChild() == n) || parent.isVar() || (parent.isFunction() && parent.getFirstChild() == n) || parent.isDec() || parent.isInc() || parent.isParamList() || parent.isCatch(); } /** * Determines whether a node represents an object literal key * (e.g. key1 in {key1: value1, key2: value2}). * * @param node A node * @param parent The node's parent */ static boolean isObjectLitKey(Node node, Node parent) { switch (node.getType()) { case Token.STRING: return parent.isObjectLit(); case Token.GETTER_DEF: case Token.SETTER_DEF: return true; } return false; } /** * Get the name of an object literal key. * * @param key A node */ static String getObjectLitKeyName(Node key) { switch (key.getType()) { case Token.STRING: case Token.GETTER_DEF: case Token.SETTER_DEF: return key.getString(); } throw new IllegalStateException("Unexpected node type: " + key); } /** * @param key A OBJECTLIT key node. * @return The type expected when using the key. */ static JSType getObjectLitKeyTypeFromValueType(Node key, JSType valueType) { if (valueType != null) { switch (key.getType()) { case Token.GETTER_DEF: // GET must always return a function type. if (valueType.isFunctionType()) { FunctionType fntype = valueType.toMaybeFunctionType(); valueType = fntype.getReturnType(); } else { return null; } break; case Token.SETTER_DEF: if (valueType.isFunctionType()) { // SET must always return a function type. FunctionType fntype = valueType.toMaybeFunctionType(); Node param = fntype.getParametersNode().getFirstChild(); // SET function must always have one parameter. valueType = param.getJSType(); } else { return null; } break; } } return valueType; } /** * Determines whether a node represents an object literal get or set key

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> the operator's token value to convert * @return the string representation * @throws Error if the token value is not an operator */ static String opToStrNoFail(int operator) { String res = opToStr(operator); if (res == null) { throw new Error("Unknown op " + operator + ": " + Token.name(operator)); } return res; } /** * @return true if n or any of its children are of the specified type */ static boolean containsType(Node node, int type, Predicate<Node> traverseChildrenPred) { return has(node, new MatchNodeType(type), traverseChildrenPred); } /** * @return true if n or any of its children are of the specified type */ static boolean containsType(Node node, int type) { return containsType(node, type, Predicates.<Node>alwaysTrue()); } /** * Given a node tree, finds all the VAR declarations in that tree that are * not in an inner scope. Then adds a new VAR node at the top of the current * scope that redeclares them, if necessary. */ static void redeclareVarsInsideBranch(Node branch) { Collection<Node> vars = getVarsDeclaredInBranch(branch); if (vars.isEmpty()) { return; } Node parent = getAddingRoot(branch); for (Node nameNode : vars) { Node var = new Node( Token.VAR, Node.newString(Token.NAME, nameNode.getString()) .copyInformationFrom(nameNode)) .copyInformationFrom(nameNode); copyNameAnnotations(nameNode, var.getFirstChild()); parent.addChildToFront(var); } } /** * Copy any annotations that follow a named value. * @param source * @param destination */ static void copyNameAnnotations(Node source, Node destination) { if (source.getBooleanProp(Node.IS_CONSTANT_NAME)) { destination.putBooleanProp(Node.IS_CONSTANT_NAME, true); } } /** * Gets a Node at the top of the current scope where we can add new var * declarations as children. */ private static Node getAddingRoot(Node n) { Node addingRoot = null; Node ancestor = n; while (null != (ancestor = ancestor.getParent())) { int type = ancestor.getType(); if (type == Token.SCRIPT) { addingRoot = ancestor; break; } else if (type == Token.FUNCTION) { addingRoot = ancestor.getLastChild(); break; } } // make sure that the adding root looks ok Preconditions.checkState(addingRoot.isBlock() || addingRoot.isScript()); Preconditions.checkState(addingRoot.getFirstChild() == null || !addingRoot.getFirstChild().isScript

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> originalName); return nameNode; } /** Test if all characters in the string are in the Basic Latin (aka ASCII) * character set - that they have UTF-16 values equal to or below 0x7f. * This check can find which identifiers with Unicode characters need to be * escaped in order to allow resulting files to be processed by non-Unicode * aware UNIX tools and editors. * * * See http://en.wikipedia.org/wiki/Latin_characters_in_Unicode * for more on Basic Latin. * * @param s The string to be checked for ASCII-goodness. * * @return True if all characters in the string are in Basic Latin set. */ static boolean isLatin(String s) { char LARGEST_BASIC_LATIN = 0x7f; int len = s.length(); for (int index = 0; index < len; index++) { char c = s.charAt(index); if (c > LARGEST_BASIC_LATIN) { return false; } } return true; } /** * Determines whether the given name is a valid variable name. */ public static boolean isValidSimpleName(String name) { return TokenStream.isJSIdentifier(name) && !TokenStream.isKeyword(name) && // no Unicode escaped characters - some browsers are less tolerant // of Unicode characters that might be valid according to the // language spec. // Note that by this point, unicode escapes have been converted // to UTF-16 characters, so we're only searching for character // values, not escapes. isLatin(name); } /** * Determines whether the given name is a valid qualified name. */ // TODO(nicksantos): This should be moved into a "Language" API, // so that the results are different for es5 and es3. public static boolean isValidQualifiedName(String name) { if (name.endsWith(".") || name.startsWith(".")) { return false; } String[] parts = name.split("\\."); for (String part : parts) { if (!isValidSimpleName(part)) { return false; } } return true; } /** * Determines whether the given name can appear on the right side of * the dot operator. Many properties (like reserved words) cannot. */ static boolean isValidPropertyName(String name) { return isValidSimpleName(name); } private static class VarCollector implements Visitor { final Map<String, Node> vars = Maps.newLinkedHashMap(); @Override public void visit(Node n) { if (n.isName()) { Node parent = n.getParent(); if (parent != null && parent.isVar()) { String name = n.getString(); if (!vars.containsKey(

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>name)) { vars.put(name, n); } } } } } /** * Retrieves vars declared in the current node tree, excluding descent scopes. */ public static Collection<Node> getVarsDeclaredInBranch(Node root) { VarCollector collector = new VarCollector(); visitPreOrder( root, collector, MATCH_NOT_FUNCTION); return collector.vars.values(); } /** * @return {@code true} if the node an assignment to a prototype property of * some constructor. */ static boolean isPrototypePropertyDeclaration(Node n) { if (!isExprAssign(n)) { return false; } return isPrototypeProperty(n.getFirstChild().getFirstChild()); } static boolean isPrototypeProperty(Node n) { String lhsString = n.getQualifiedName(); if (lhsString == null) { return false; } int prototypeIdx = lhsString.indexOf(".prototype."); return prototypeIdx != -1; } /** * @return The class name part of a qualified prototype name. */ static Node getPrototypeClassName(Node qName) { Node cur = qName; while (cur.isGetProp()) { if (cur.getLastChild().getString().equals("prototype")) { return cur.getFirstChild(); } else { cur = cur.getFirstChild(); } } return null; } /** * @return The string property name part of a qualified prototype name. */ static String getPrototypePropertyName(Node qName) { String qNameStr = qName.getQualifiedName(); int prototypeIdx = qNameStr.lastIndexOf(".prototype."); int memberIndex = prototypeIdx + ".prototype".length() + 1; return qNameStr.substring(memberIndex); } /** * Create a node for an empty result expression: * "void 0" */ static Node newUndefinedNode(Node srcReferenceNode) { Node node = new Node(Token.VOID, Node.newNumber(0)); if (srcReferenceNode != null) { node.copyInformationFromForTree(srcReferenceNode); } return node; } /** * Create a VAR node containing the given name and initial value expression. */ static Node newVarNode(String name, Node value) { Node nodeName = Node.newString(Token.NAME, name); if (value != null) { Preconditions.checkState(value.getNext() == null); nodeName.addChildToBack(value); nodeName.copyInformationFrom(value); } Node var = new Node(Token.VAR, nodeName) .copyInformationFrom(nodeName); return var; } /** * A predicate for matching name nodes with the specified node. */ private static class MatchNameNode implements Predicate<Node>{ final String name; MatchNameNode

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>(String name){ this.name = name; } @Override public boolean apply(Node n) { return n.isName() && n.getString().equals(name); } } /** * A predicate for matching nodes with the specified type. */ static class MatchNodeType implements Predicate<Node>{ final int type; MatchNodeType(int type){ this.type = type; } @Override public boolean apply(Node n) { return n.getType() == type; } } /** * A predicate for matching var or function declarations. */ static class MatchDeclaration implements Predicate<Node> { @Override public boolean apply(Node n) { return isFunctionDeclaration(n) || n.isVar(); } } /** * A predicate for matching anything except function nodes. */ private static class MatchNotFunction implements Predicate<Node>{ @Override public boolean apply(Node n) { return !n.isFunction(); } } static final Predicate<Node> MATCH_NOT_FUNCTION = new MatchNotFunction(); /** * A predicate for matching statements without exiting the current scope. */ static class MatchShallowStatement implements Predicate<Node>{ @Override public boolean apply(Node n) { Node parent = n.getParent(); return n.isBlock() || (!n.isFunction() && (parent == null || isControlStructure(parent) || isStatementBlock(parent))); } } /** * Finds the number of times a type is referenced within the node tree. */ static int getNodeTypeReferenceCount( Node node, int type, Predicate<Node> traverseChildrenPred) { return getCount(node, new MatchNodeType(type), traverseChildrenPred); } /** * Whether a simple name is referenced within the node tree. */ static boolean isNameReferenced(Node node, String name, Predicate<Node> traverseChildrenPred) { return has(node, new MatchNameNode(name), traverseChildrenPred); } /** * Whether a simple name is referenced within the node tree. */ static boolean isNameReferenced(Node node, String name) { return isNameReferenced(node, name, Predicates.<Node>alwaysTrue()); } /** * Finds the number of times a simple name is referenced within the node tree. */ static int getNameReferenceCount(Node node, String name) { return getCount( node, new MatchNameNode(name), Predicates.<Node>alwaysTrue()); } /** * @return Whether the predicate is true for the node or any of its children. */ static boolean has(Node node, Predicate<Node> pred, Predicate<Node> traverseChildrenPred) { if (pred.apply(node)) { return true; } if (!traverseChildrenPred.apply(node)) { return false;

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> } for (Node c = node.getFirstChild(); c != null; c = c.getNext()) { if (has(c, pred, traverseChildrenPred)) { return true; } } return false; } /** * @return The number of times the the predicate is true for the node * or any of its children. */ static int getCount( Node n, Predicate<Node> pred, Predicate<Node> traverseChildrenPred) { int total = 0; if (pred.apply(n)) { total++; } if (traverseChildrenPred.apply(n)) { for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { total += getCount(c, pred, traverseChildrenPred); } } return total; } /** * Interface for use with the visit method. * @see #visit */ static interface Visitor { void visit(Node node); } /** * A pre-order traversal, calling Vistor.visit for each child matching * the predicate. */ static void visitPreOrder(Node node, Visitor vistor, Predicate<Node> traverseChildrenPred) { vistor.visit(node); if (traverseChildrenPred.apply(node)) { for (Node c = node.getFirstChild(); c != null; c = c.getNext()) { visitPreOrder(c, vistor, traverseChildrenPred); } } } /** * A post-order traversal, calling Vistor.visit for each child matching * the predicate. */ static void visitPostOrder(Node node, Visitor vistor, Predicate<Node> traverseChildrenPred) { if (traverseChildrenPred.apply(node)) { for (Node c = node.getFirstChild(); c != null; c = c.getNext()) { visitPostOrder(c, vistor, traverseChildrenPred); } } vistor.visit(node); } /** * @return Whether a TRY node has a finally block. */ static boolean hasFinally(Node n) { Preconditions.checkArgument(n.isTry()); return n.getChildCount() == 3; } /** * @return The BLOCK node containing the CATCH node (if any) * of a TRY. */ static Node getCatchBlock(Node n) { Preconditions.checkArgument(n.isTry()); return n.getFirstChild().getNext(); } /** * @return Whether BLOCK (from a TRY node) contains a CATCH. * @see NodeUtil#getCatchBlock */ static boolean hasCatchHandler(Node n) { Preconditions.checkArgument(n.isBlock()); return n.hasChildren() && n.getFirstChild().isCatch(); } /** * @param fnNode The function. * @return

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> The Node containing the Function parameters. */ public static Node getFunctionParameters(Node fnNode) { // Function NODE: [ FUNCTION -> NAME, LP -> ARG1, ARG2, ... ] Preconditions.checkArgument(fnNode.isFunction()); return fnNode.getFirstChild().getNext(); } /** * Returns true if a name node represents a constant variable. * * <p>Determining whether a variable is constant has three steps: * <ol> * <li>In CodingConventionAnnotator, any name that matches the * {@link CodingConvention#isConstant(String)} is annotated with an * IS_CONSTANT_NAME property. * <li>The normalize pass renames any variable with the IS_CONSTANT_NAME * annotation and that is initialized to a constant value with * a variable name inlucding $$constant. * <li>Return true here if the variable includes $$constant in its name. * </ol> * * @param node A NAME or STRING node * @return True if the variable is constant */ static boolean isConstantName(Node node) { return node.getBooleanProp(Node.IS_CONSTANT_NAME); } /** Whether the given name is constant by coding convention. */ static boolean isConstantByConvention( CodingConvention convention, Node node, Node parent) { String name = node.getString(); if (parent.isGetProp() && node == parent.getLastChild()) { return convention.isConstantKey(name); } else if (isObjectLitKey(node, parent)) { return convention.isConstantKey(name); } else { return convention.isConstant(name); } } /** * Get the JSDocInfo for a function. */ public static JSDocInfo getFunctionJSDocInfo(Node n) { Preconditions.checkState(n.isFunction()); JSDocInfo fnInfo = n.getJSDocInfo(); if (fnInfo == null && NodeUtil.isFunctionExpression(n)) { // Look for the info on other nodes. Node parent = n.getParent(); if (parent.isAssign()) { // on ASSIGNs fnInfo = parent.getJSDocInfo(); } else if (parent.isName()) { // on var NAME = function() { ... }; fnInfo = parent.getParent().getJSDocInfo(); } } return fnInfo; } /** * @param n The node. * @return The source name property on the node or its ancestors. */ public static String getSourceName(Node n) { String sourceName = null; while (sourceName == null && n != null) { sourceName = n.getSourceFileName(); n = n.getParent(); } return sourceName; } /** * @param n The node. *

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> @return The source name property on the node or its ancestors. */ public static StaticSourceFile getSourceFile(Node n) { StaticSourceFile sourceName = null; while (sourceName == null && n != null) { sourceName = n.getStaticSourceFile(); n = n.getParent(); } return sourceName; } /** * @param n The node. * @return The InputId property on the node or its ancestors. */ public static InputId getInputId(Node n) { while (n != null && !n.isScript()) { n = n.getParent(); } return (n != null && n.isScript()) ? n.getInputId() : null; } /** * A new CALL node with the "FREE_CALL" set based on call target. */ static Node newCallNode(Node callTarget, Node... parameters) { boolean isFreeCall = !isGet(callTarget); Node call = new Node(Token.CALL, callTarget); call.putBooleanProp(Node.FREE_CALL, isFreeCall); for (Node parameter : parameters) { call.addChildToBack(parameter); } return call; } /** * @return Whether the node is known to be a value that is not referenced * elsewhere. */ static boolean evaluatesToLocalValue(Node value) { return evaluatesToLocalValue(value, Predicates.<Node>alwaysFalse()); } /** * @param locals A predicate to apply to unknown local values. * @return Whether the node is known to be a value that is not a reference * outside the expression scope. */ static boolean evaluatesToLocalValue(Node value, Predicate<Node> locals) { switch (value.getType()) { case Token.ASSIGN: // A result that is aliased by a non-local name, is the effectively the // same as returning a non-local name, but this doesn't matter if the // value is immutable. return NodeUtil.isImmutableValue(value.getLastChild()) || (locals.apply(value) && evaluatesToLocalValue(value.getLastChild(), locals)); case Token.COMMA: return evaluatesToLocalValue(value.getLastChild(), locals); case Token.AND: case Token.OR: return evaluatesToLocalValue(value.getFirstChild(), locals) && evaluatesToLocalValue(value.getLastChild(), locals); case Token.HOOK: return evaluatesToLocalValue(value.getFirstChild().getNext(), locals) && evaluatesToLocalValue(value.getLastChild(), locals); case Token.INC: case Token.DEC: if (value.getBooleanProp(Node.INCRDECR_PROP)) { return evaluatesToLocalValue(value.getFirstChild(), locals); } else { return true; } case Token.THIS: return locals.apply(value); case Token.NAME

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>: return isImmutableValue(value) || locals.apply(value); case Token.GETELEM: case Token.GETPROP: // There is no information about the locality of object properties. return locals.apply(value); case Token.CALL: return callHasLocalResult(value) || isToStringMethodCall(value) || locals.apply(value); case Token.NEW: return newHasLocalResult(value) || locals.apply(value); case Token.FUNCTION: case Token.REGEXP: case Token.ARRAYLIT: case Token.OBJECTLIT: // Literals objects with non-literal children are allowed. return true; case Token.DELPROP: case Token.IN: // TODO(johnlenz): should IN operator be included in #isSimpleOperator? return true; default: // Other op force a local value: // x = '' + g (x is now an local string) // x -= g (x is now an local number) if (isAssignmentOp(value) || isSimpleOperator(value) || isImmutableValue(value)) { return true; } throw new IllegalStateException( "Unexpected expression node" + value + "\n parent:" + value.getParent()); } } /** * Given the first sibling, this returns the nth * sibling or null if no such sibling exists. * This is like "getChildAtIndex" but returns null for non-existent indexes. */ private static Node getNthSibling(Node first, int index) { Node sibling = first; while (index != 0 && sibling != null) { sibling = sibling.getNext(); index--; } return sibling; } /** * Given the function, this returns the nth * argument or null if no such parameter exists. */ static Node getArgumentForFunction(Node function, int index) { Preconditions.checkState(function.isFunction()); return getNthSibling( function.getFirstChild().getNext().getFirstChild(), index); } /** * Given the new or call, this returns the nth * argument of the call or null if no such argument exists. */ static Node getArgumentForCallOrNew(Node call, int index) { Preconditions.checkState(isCallOrNew(call)); return getNthSibling( call.getFirstChild().getNext(), index); } private static boolean isToStringMethodCall(Node call) { Node getNode = call.getFirstChild(); if (isGet(getNode)) { Node propNode = getNode.getLastChild(); return propNode.isString() && "toString".equals(propNode.getString()); } return false; } /** Find the best JSDoc for the given node. */ static JSDocInfo getBestJSDocInfo(Node n) { JSDocInfo info = n.

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>getJSDocInfo(); if (info == null) { Node parent = n.getParent(); if (parent == null) { return null; } if (parent.isName()) { return getBestJSDocInfo(parent); } else if (parent.isAssign()) { info = parent.getJSDocInfo(); } else if (isObjectLitKey(parent, parent.getParent())) { info = parent.getJSDocInfo(); } else if (parent.isFunction()) { info = parent.getJSDocInfo(); } else if (parent.isVar() && parent.hasOneChild()) { info = parent.getJSDocInfo(); } } return info; } /** Find the l-value that the given r-value is being assigned to. */ static Node getBestLValue(Node n) { Node parent = n.getParent(); boolean isFunctionDeclaration = isFunctionDeclaration(n); if (isFunctionDeclaration) { return n.getFirstChild(); } else if (parent.isName()) { return parent; } else if (parent.isAssign()) { return parent.getFirstChild(); } else if (isObjectLitKey(parent, parent.getParent())) { return parent; } return null; } /** Get the owner of the given l-value node. */ static Node getBestLValueOwner(@Nullable Node lValue) { if (lValue == null || lValue.getParent() == null) { return null; } if (isObjectLitKey(lValue, lValue.getParent())) { return getBestLValue(lValue.getParent()); } else if (isGet(lValue)) { return lValue.getFirstChild(); } return null; } /** Get the name of the given l-value node. */ static String getBestLValueName(@Nullable Node lValue) { if (lValue == null || lValue.getParent() == null) { return null; } if (isObjectLitKey(lValue, lValue.getParent())) { Node owner = getBestLValue(lValue.getParent()); if (owner != null) { String ownerName = getBestLValueName(owner); if (ownerName != null) { return ownerName + "." + getObjectLitKeyName(lValue); } } return null; } return lValue.getQualifiedName(); } /** * @returns false iff the result of the expression is not consumed. */ static boolean isExpressionResultUsed(Node expr) { // TODO(johnlenz): consider sharing some code with trySimpleUnusedResult. Node parent = expr.getParent(); switch (parent.getType()) { case Token.EXPR_RESULT: return false; case Token.HOOK: case Token.AND: case Token.OR: return (

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>Callback implements HotSwapCompilerPass { private final AbstractCompiler compiler; @SuppressWarnings("unused") private boolean inExterns; InferJSDocInfo(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { if (externs != null) { inExterns = true; NodeTraversal.traverse(compiler, externs, this); } if (root != null) { inExterns = false; NodeTraversal.traverse(compiler, root, this); } } @Override public void hotSwapScript(Node root, Node originalRoot) { Preconditions.checkNotNull(root); Preconditions.checkState(root.isScript()); inExterns = false; NodeTraversal.traverse(compiler, root, this); } @Override public void visit(NodeTraversal t, Node n, Node parent) { JSDocInfo docInfo; switch (n.getType()) { // Infer JSDocInfo on types of all type declarations on variables. case Token.NAME: if (parent == null) { return; } // Only allow JSDoc on VARs, function declarations, and assigns. if (parent.getType() != Token.VAR && !NodeUtil.isFunctionDeclaration(parent) && !(parent.isAssign() && n == parent.getFirstChild())) { return; } // There are four places the doc info could live. // 1) A FUNCTION node. // /** ... */ function f() { ... } // 2) An ASSIGN parent. // /** ... */ x = function () { ... } // 3) A NAME parent. // var x, /** ... */ y = function() { ... } // 4) A VAR gramps. // /** ... */ var x = function() { ... } docInfo = n.getJSDocInfo(); if (docInfo == null && !(parent.isVar() && !parent.hasOneChild())) { docInfo = parent.getJSDocInfo(); } // Try to find the type of the NAME. JSType varType = n.getJSType(); if (varType == null && parent.isFunction()) { varType = parent.getJSType(); } // If we have no type to attach JSDocInfo to, then there's nothing // we can do. if (varType == null || docInfo == null) { return; } // Dereference the type. If the result is not an object, or already // has docs attached, then do nothing. ObjectType objType = dereferenceToObject(varType); if (objType == null || objType.getJSDocInfo() != null) { return; } attachJSDocInfoToNominalTypeOr

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>Shape(objType, docInfo, n.getString()); break; case Token.GETPROP: // Infer JSDocInfo on properties. // There are two ways to write doc comments on a property. // // 1) // /** @deprecated */ // obj.prop = ... // // 2) // /** @deprecated */ // obj.prop; if (NodeUtil.isExpressionNode(parent) || (parent.isAssign() && parent.getFirstChild() == n)) { docInfo = n.getJSDocInfo(); if (docInfo == null) { docInfo = parent.getJSDocInfo(); } if (docInfo != null) { ObjectType lhsType = dereferenceToObject(n.getFirstChild().getJSType()); if (lhsType != null) { // Put the JSDoc in the property slot, if there is one. String propName = n.getLastChild().getString(); if (lhsType.hasOwnProperty(propName)) { lhsType.setPropertyJSDocInfo(propName, docInfo); } // Put the JSDoc in any constructors or function shapes as well. ObjectType propType = dereferenceToObject(lhsType.getPropertyType(propName)); if (propType != null) { attachJSDocInfoToNominalTypeOrShape( propType, docInfo, n.getQualifiedName()); } } } } break; } } /** * Dereferences the given type to an object, or returns null. */ private ObjectType dereferenceToObject(JSType type) { return ObjectType.cast(type == null ? null : type.dereference()); } /** * Handle cases #1 and #3 in the class doc. */ private void attachJSDocInfoToNominalTypeOrShape( ObjectType objType, JSDocInfo docInfo, @Nullable String qName) { if (objType.isConstructor() || objType.isEnumType() || objType.isInterface()) { // Named types. if (objType.hasReferenceName() && objType.getReferenceName().equals(qName)) { objType.setJSDocInfo(docInfo); if (objType.isConstructor() || objType.isInterface()) { JSType.toMaybeFunctionType(objType).getInstanceType().setJSDocInfo( docInfo); } else if (objType instanceof EnumType) { ((EnumType) objType).getElementsType().setJSDocInfo(docInfo); } } } else if (!objType.isNativeObjectType() && objType.isFunctionType()) { // Structural functions. objType.setJSDocInfo(docInfo); } } }

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>weakInfo> tweakInfos) { for (Entry<String, Node> entry : compilerDefaultValueOverrides.entrySet()) { String tweakId = entry.getKey(); TweakInfo tweakInfo = tweakInfos.get(tweakId); if (tweakInfo == null) { compiler.report(JSError.make(UNKNOWN_TWEAK_WARNING, tweakId)); } else { TweakFunction registerFunc = tweakInfo.registerCall.tweakFunc; Node value = entry.getValue(); if (!registerFunc.isValidNodeType(value.getType())) { compiler.report(JSError.make(INVALID_TWEAK_DEFAULT_VALUE_WARNING, tweakId, registerFunc.getName(), registerFunc.getExpectedTypeName())); } else { tweakInfo.defaultValueNode = value; } } } } /** * Finds all calls to goog.tweak functions and emits warnings/errors if any * of the calls have issues. * @return A map of {@link TweakInfo} structures, keyed by tweak ID. */ private CollectTweaksResult collectTweaks(Node root) { CollectTweaks pass = new CollectTweaks(); NodeTraversal.traverse(compiler, root, pass); Map<String, TweakInfo> tweakInfos = pass.allTweaks; for (TweakInfo tweakInfo: tweakInfos.values()) { tweakInfo.emitAllWarnings(); } return new CollectTweaksResult(tweakInfos, pass.getOverridesCalls); } private final static class CollectTweaksResult { final Map<String, TweakInfo> tweakInfos; final List<TweakFunctionCall> getOverridesCalls; CollectTweaksResult(Map<String, TweakInfo> tweakInfos, List<TweakFunctionCall> getOverridesCalls) { this.tweakInfos = tweakInfos; this.getOverridesCalls = getOverridesCalls; } } /** * Processes all calls to goog.tweak functions. */ private final class CollectTweaks extends AbstractPostOrderCallback { final Map<String, TweakInfo> allTweaks = Maps.newHashMap(); final List<TweakFunctionCall> getOverridesCalls = Lists.newArrayList(); @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() != Token.CALL) { return; } String callName = n.getFirstChild().getQualifiedName(); TweakFunction tweakFunc = TWEAK_FUNCTIONS_MAP.get(callName); if (tweakFunc == null) { return; } if (tweakFunc == TweakFunction.GET_COMPILER_OVERRIDES) { getOverridesCalls.add( new TweakFunctionCall(t.getSourceName(), tweakFunc, n)); return; } // Ensure the first parameter (the tweak ID) is a string

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> literal. Node tweakIdNode = n.getFirstChild().getNext(); if (tweakIdNode.getType() != Token.STRING) { compiler.report(t.makeError(tweakIdNode, NON_LITERAL_TWEAK_ID_ERROR)); return; } String tweakId = tweakIdNode.getString(); // Make sure there is a TweakInfo structure for it. TweakInfo tweakInfo = allTweaks.get(tweakId); if (tweakInfo == null) { tweakInfo = new TweakInfo(tweakId); allTweaks.put(tweakId, tweakInfo); } switch (tweakFunc) { case REGISTER_BOOLEAN: case REGISTER_NUMBER: case REGISTER_STRING: // Ensure the ID contains only valid characters. if (!ID_MATCHER.matchesAllOf(tweakId)) { compiler.report(t.makeError(tweakIdNode, INVALID_TWEAK_ID_ERROR)); } // Ensure tweaks are registered in the global scope. if (!t.inGlobalScope()) { compiler.report( t.makeError(n, NON_GLOBAL_TWEAK_INIT_ERROR, tweakId)); break; } // Ensure tweaks are registered only once. if (tweakInfo.isRegistered()) { compiler.report( t.makeError(n, TWEAK_MULTIPLY_REGISTERED_ERROR, tweakId)); break; } Node tweakDefaultValueNode = tweakIdNode.getNext().getNext(); tweakInfo.addRegisterCall(t.getSourceName(), tweakFunc, n, tweakDefaultValueNode); break; case OVERRIDE_DEFAULT_VALUE: // Ensure tweaks overrides occur in the global scope. if (!t.inGlobalScope()) { compiler.report( t.makeError(n, NON_GLOBAL_TWEAK_INIT_ERROR, tweakId)); break; } // Ensure tweak overrides occur before the tweak is registered. if (tweakInfo.isRegistered()) { compiler.report( t.makeError(n, TWEAK_OVERRIDE_AFTER_REGISTERED_ERROR, tweakId)); break; } tweakDefaultValueNode = tweakIdNode.getNext(); tweakInfo.addOverrideDefaultValueCall(t.getSourceName(), tweakFunc, n, tweakDefaultValueNode); break; case GET_BOOLEAN: case GET_NUMBER: case GET_STRING: tweakInfo.addGetterCall(t.getSourceName(), tweakFunc, n); } } } /** * Holds information about a call to a goog.tweak function. */ private static final class TweakFunctionCall { final String sourceName; final TweakFunction tweakFunc; final Node callNode; final Node valueNode; TweakFunctionCall(String sourceName, TweakFunction tweakFunc, Node call

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>/* * Copyright 2006 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.javascript.rhino.Node; /** * This interface defines how objects capable of creating scopes from the parse * tree behave. * */ interface ScopeCreator { /** * Creates a {@link Scope} object. * * @param n the root node (either a FUNCTION node, a SCRIPT node, or a * synthetic block node whose children are all SCRIPT nodes) * @param parent the parent Scope object (may be null) */ Scope createScope(Node n, Scope parent); }

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>isConstructor() || info.isInterface(); } /** * The scope that we should declare this function in, if it needs * to be declared in a scope. Notice that TypedScopeCreator takes * care of most scope-declaring. */ private Scope getScopeDeclaredIn() { int dotIndex = fnName.indexOf("."); if (dotIndex != -1) { String rootVarName = fnName.substring(0, dotIndex); Var rootVar = scope.getVar(rootVarName); if (rootVar != null) { return rootVar.getScope(); } } return scope; } /** * Check whether a type is resolvable in the future * If this has a supertype that hasn't been resolved yet, then we can assume * this type will be ok once the super type resolves. * @param objectType * @return true if objectType is resolvable in the future */ private static boolean hasMoreTagsToResolve(ObjectType objectType) { Preconditions.checkArgument(objectType.isUnknownType()); if (objectType.getImplicitPrototype() != null) { // constructor extends class if (objectType.getImplicitPrototype().isResolved()) { return false; } else { return true; } } else { // interface extends interfaces FunctionType ctor = objectType.getConstructor(); if (ctor != null) { for (ObjectType interfaceType : ctor.getExtendedInterfaces()) { if (!interfaceType.isResolved()) { return true; } } } return false; } } /** Holds data dynamically inferred about functions. */ static interface FunctionContents { /** Returns the source node of this function. May be null. */ Node getSourceNode(); /** Returns if the function may be in externs. */ boolean mayBeFromExterns(); /** Returns if a return of a real value (not undefined) appears. */ boolean mayHaveNonEmptyReturns(); /** Gets a list of variables in this scope that are escaped. */ Iterable<String> getEscapedVarNames(); } static class UnknownFunctionContents implements FunctionContents { private static UnknownFunctionContents singleton = new UnknownFunctionContents(); static FunctionContents get() { return singleton; } @Override public Node getSourceNode() { return null; } @Override public boolean mayBeFromExterns() { return true; } @Override public boolean mayHaveNonEmptyReturns() { return true; } @Override public Iterable<String> getEscapedVarNames() { return ImmutableList.of(); } } static class AstFunctionContents implements FunctionContents { private final Node n; private boolean hasNonEmptyReturns = false; private Set<String> escapedVarNames; AstFunctionContents(Node n) { this.n = n; }

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> @Override public Node getSourceNode() { return n; } @Override public boolean mayBeFromExterns() { return n.isFromExterns(); } @Override public boolean mayHaveNonEmptyReturns() { return hasNonEmptyReturns; } void recordNonEmptyReturn() { hasNonEmptyReturns = true; } @Override public Iterable<String> getEscapedVarNames() { return escapedVarNames == null ? ImmutableList.<String>of() : escapedVarNames; } void recordEscapedVarName(String name) { if (escapedVarNames == null) { escapedVarNames = Sets.newHashSet(); } escapedVarNames.add(name); } } }

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> Node subclass = null; Node superclass = callNode.getLastChild(); // There are six possible syntaxes for a class-defining method: // SubClass.inherits(SuperClass) // goog.inherits(SubClass, SuperClass) // goog$inherits(SubClass, SuperClass) // SubClass.mixin(SuperClass.prototype) // goog.mixin(SubClass.prototype, SuperClass.prototype) // goog$mixin(SubClass.prototype, SuperClass.prototype) boolean isDeprecatedCall = callNode.getChildCount() == 2 && callName.isGetProp(); if (isDeprecatedCall) { // SubClass.inherits(SuperClass) subclass = callName.getFirstChild(); } else if (callNode.getChildCount() == 3) { // goog.inherits(SubClass, SuperClass) subclass = callName.getNext(); } else { return null; } if (type == SubclassType.MIXIN) { // Only consider mixins that mix two prototypes as related to // inheritance. if (!endsWithPrototype(superclass)) { return null; } if (!isDeprecatedCall) { if (!endsWithPrototype(subclass)) { return null; } // Strip off the prototype from the name. subclass = subclass.getFirstChild(); } superclass = superclass.getFirstChild(); } // bail out if either of the side of the "inherits" // isn't a real class name. This prevents us from // doing something weird in cases like: // goog.inherits(MySubClass, cond ? SuperClass1 : BaseClass2) if (subclass != null && subclass.isUnscopedQualifiedName() && superclass.isUnscopedQualifiedName()) { return new SubclassRelationship(type, subclass, superclass); } } return null; } /** * Determines whether the given node is a class-defining name, like * "inherits" or "mixin." * @return The type of class-defining name, or null. */ private SubclassType typeofClassDefiningName(Node callName) { // Check if the method name matches one of the class-defining methods. String methodName = null; if (callName.isGetProp()) { methodName = callName.getLastChild().getString(); } else if (callName.isName()) { String name = callName.getString(); int dollarIndex = name.lastIndexOf('$'); if (dollarIndex != -1) { methodName = name.substring(dollarIndex + 1); } } if (methodName != null) { if (methodName.equals("inherits")) { return SubclassType.INHERITS; } else if (methodName.equals("mixin")) { return SubclassType.MIXIN; } } return null; } @Override public boolean isSuperClass

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>Reference(String propertyName) { return "superClass_".equals(propertyName); } /** * Given a qualified name node, returns whether "prototype" is at the end. * For example: * a.b.c => false * a.b.c.prototype => true */ private boolean endsWithPrototype(Node qualifiedName) { return qualifiedName.isGetProp() && qualifiedName.getLastChild().getString().equals("prototype"); } /** * Exctracts X from goog.provide('X'), if the applied Node is goog. * * @return The extracted class name, or null. */ @Override public String extractClassNameIfProvide(Node node, Node parent){ return extractClassNameIfGoog(node, parent, "goog.provide"); } /** * Exctracts X from goog.require('X'), if the applied Node is goog. * * @return The extracted class name, or null. */ @Override public String extractClassNameIfRequire(Node node, Node parent){ return extractClassNameIfGoog(node, parent, "goog.require"); } private static String extractClassNameIfGoog(Node node, Node parent, String functionName){ String className = null; if (NodeUtil.isExprCall(parent)) { Node callee = node.getFirstChild(); if (callee != null && callee.isGetProp()) { String qualifiedName = callee.getQualifiedName(); if (functionName.equals(qualifiedName)) { Node target = callee.getNext(); if (target != null && target.isString()) { className = target.getString(); } } } } return className; } /** * Use closure's implementation. * @return closure's function name for exporting properties. */ @Override public String getExportPropertyFunction() { return "goog.exportProperty"; } /** * Use closure's implementation. * @return closure's function name for exporting symbols. */ @Override public String getExportSymbolFunction() { return "goog.exportSymbol"; } @Override public List<String> identifyTypeDeclarationCall(Node n) { Node callName = n.getFirstChild(); if ("goog.addDependency".equals(callName.getQualifiedName()) && n.getChildCount() >= 3) { Node typeArray = callName.getNext().getNext(); if (typeArray.getType() == Token.ARRAYLIT) { List<String> typeNames = Lists.newArrayList(); for (Node name = typeArray.getFirstChild(); name != null; name = name.getNext()) { if (name.isString()) { typeNames.add(name.getString()); } } return typeNames; } } return null; } @Override public String getAbstractMethodName() { return "goog

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>TYPE), new AssertionFunctionSpec("goog.asserts.assertString", JSTypeNative.STRING_TYPE), new AssertionFunctionSpec("goog.asserts.assertFunction", JSTypeNative.FUNCTION_INSTANCE_TYPE), new AssertionFunctionSpec("goog.asserts.assertObject", JSTypeNative.OBJECT_TYPE), new AssertionFunctionSpec("goog.asserts.assertArray", JSTypeNative.ARRAY_TYPE), // TODO(agrieve): It would be better if this could make the first // parameter the type of the second parameter. new AssertionFunctionSpec("goog.asserts.assertInstanceof", JSTypeNative.OBJECT_TYPE) ); } @Override public Bind describeFunctionBind(Node n) { Bind result = super.describeFunctionBind(n); if (result != null) { return result; } // It would be nice to be able to identify a fn.bind call // but that requires knowing the type of "fn". if (n.getType() != Token.CALL) { return null; } Node callTarget = n.getFirstChild(); String name = callTarget.getQualifiedName(); if (name != null) { if (name.equals("goog.bind") || name.equals("goog$bind")) { // goog.bind(fn, self, args...); Node fn = callTarget.getNext(); if (fn == null) { return null; } Node thisValue = safeNext(fn); Node parameters = safeNext(thisValue); return new Bind(fn, thisValue, parameters); } if (name.equals("goog.partial") || name.equals("goog$partial")) { // goog.partial(fn, args...); Node fn = callTarget.getNext(); if (fn == null) { return null; } Node thisValue = null; Node parameters = safeNext(fn); return new Bind(fn, thisValue, parameters); } } return null; } private Node safeNext(Node n) { if (n != null) { return n.getNext(); } return null; } }

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> = this; if (null != this.last.next) { // fail early on loops. implies same node in array twice throw new IllegalArgumentException("duplicate child"); } } } public static Node newNumber(double number) { return new NumberNode(number); } public static Node newNumber(double number, int lineno, int charno) { return new NumberNode(number, lineno, charno); } public static Node newString(String str) { return new StringNode(Token.STRING, str); } public static Node newString(int type, String str) { return new StringNode(type, str); } public static Node newString(String str, int lineno, int charno) { return new StringNode(Token.STRING, str, lineno, charno); } public static Node newString(int type, String str, int lineno, int charno) { return new StringNode(type, str, lineno, charno); } public int getType() { return type; } public void setType(int type) { this.type = type; } public boolean hasChildren() { return first != null; } public Node getFirstChild() { return first; } public Node getLastChild() { return last; } public Node getNext() { return next; } public Node getChildBefore(Node child) { if (child == first) { return null; } Node n = first; while (n.next != child) { n = n.next; if (n == null) { throw new RuntimeException("node is not a child"); } } return n; } public Node getChildAtIndex(int i) { Node n = first; while (i > 0) { n = n.next; i--; } return n; } public int getIndexOfChild(Node child) { Node n = first; int i = 0; while (n != null) { if (child == n) { return i; } n = n.next; i++; } return -1; } public Node getLastSibling() { Node n = this; while (n.next != null) { n = n.next; } return n; } public void addChildToFront(Node child) { Preconditions.checkArgument(child.parent == null); Preconditions.checkArgument(child.next == null); child.parent = this; child.next = first; first = child; if (last == null) { last = child; } } public void addChildToBack(Node child) { Preconditions.checkArgument(child.parent == null); Preconditions.checkArgument(child.next == null); child.

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> { default: value = x.toString(); break; } sb.append(value); sb.append(']'); } } if (printType) { if (jsType != null) { String jsTypeString = jsType.toString(); if (jsTypeString != null) { sb.append(" : "); sb.append(jsTypeString); } } } } public String toStringTree() { return toStringTreeImpl(); } private String toStringTreeImpl() { try { StringBuilder s = new StringBuilder(); appendStringTree(s); return s.toString(); } catch (IOException e) { throw new RuntimeException("Should not happen\n" + e); } } public void appendStringTree(Appendable appendable) throws IOException { toStringTreeHelper(this, 0, appendable); } private static void toStringTreeHelper(Node n, int level, Appendable sb) throws IOException { for (int i = 0; i != level; ++i) { sb.append(" "); } sb.append(n.toString()); sb.append('\n'); for (Node cursor = n.getFirstChild(); cursor != null; cursor = cursor.getNext()) { toStringTreeHelper(cursor, level + 1, sb); } } int type; // type of the node; Token.NAME for example Node next; // next sibling private Node first; // first element of a linked list of children private Node last; // last element of a linked list of children /** * Linked list of properties. Since vast majority of nodes would have * no more then 2 properties, linked list saves memory and provides * fast lookup. If this does not holds, propListHead can be replaced * by UintMap. */ private PropListItem propListHead; /** * COLUMN_BITS represents how many of the lower-order bits of * sourcePosition are reserved for storing the column number. * Bits above these store the line number. * This gives us decent position information for everything except * files already passed through a minimizer, where lines might * be longer than 4096 characters. */ public static final int COLUMN_BITS = 12; /** * MAX_COLUMN_NUMBER represents the maximum column number that can * be represented. JSCompiler's modifications to Rhino cause all * tokens located beyond the maximum column to MAX_COLUMN_NUMBER. */ public static final int MAX_COLUMN_NUMBER = (1 << COLUMN_BITS) - 1; /** * COLUMN_MASK stores a value where bits storing the column number * are set, and bits storing the line are not set. It's handy for * separating column number from line number. */ public static final int COLUMN_MASK = MAX_COLUMN_NUMBER;

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> final class SiblingNodeIterable implements Iterable<Node>, Iterator<Node> { private final Node start; private Node current; private boolean used; SiblingNodeIterable(Node start) { this.start = start; this.current = start; this.used = false; } @Override public Iterator<Node> iterator() { if (!used) { used = true; return this; } else { // We have already used the current object as an iterator; // we must create a new SiblingNodeIterable based on this // iterable's start node. // // Since the primary use case for Node.children is in for // loops, this branch is extremely unlikely. return (new SiblingNodeIterable(start)).iterator(); } } @Override public boolean hasNext() { return current != null; } @Override public Node next() { if (current == null) { throw new NoSuchElementException(); } try { return current; } finally { current = current.getNext(); } } @Override public void remove() { throw new UnsupportedOperationException(); } } // ========================================================================== // Accessors PropListItem getPropListHeadForTesting() { return propListHead; } public Node getParent() { return parent; } /** * Gets the ancestor node relative to this. * * @param level 0 = this, 1 = the parent, etc. */ public Node getAncestor(int level) { Preconditions.checkArgument(level >= 0); Node node = this; while (node != null && level-- > 0) { node = node.getParent(); } return node; } /** * Iterates all of the node's ancestors excluding itself. */ public AncestorIterable getAncestors() { return new AncestorIterable(this.getParent()); } /** * Iterator to go up the ancestor tree. */ public static class AncestorIterable implements Iterable<Node> { private Node cur; /** * @param cur The node to start. */ AncestorIterable(Node cur) { this.cur = cur; } @Override public Iterator<Node> iterator() { return new Iterator<Node>() { @Override public boolean hasNext() { return cur != null; } @Override public Node next() { if (!hasNext()) throw new NoSuchElementException(); Node n = cur; cur = cur.getParent(); return n; } @Override public void remove() { throw new UnsupportedOperationException(); } }; } } /** * Check for one child more efficiently than by iterating over all the * children as is done with Node.getChildCount(). * * @return Whether the node has exactly one child. */ public boolean hasOneChild() {

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> return first != null && first == last; } /** * Check for more than one child more efficiently than by iterating over all * the children as is done with Node.getChildCount(). * * @return Whether the node more than one child. */ public boolean hasMoreThanOneChild() { return first != null && first != last; } public int getChildCount() { int c = 0; for (Node n = first; n != null; n = n.next) c++; return c; } // Intended for testing and verification only. public boolean hasChild(Node child) { for (Node n = first; n != null; n = n.getNext()) { if (child == n) { return true; } } return false; } /** * Checks if the subtree under this node is the same as another subtree. * Returns null if it's equal, or a message describing the differences. */ public String checkTreeEquals(Node node2) { NodeMismatch diff = checkTreeEqualsImpl(node2); if (diff != null) { return "Node tree inequality:" + "\nTree1:\n" + toStringTree() + "\n\nTree2:\n" + node2.toStringTree() + "\n\nSubtree1: " + diff.nodeA.toStringTree() + "\n\nSubtree2: " + diff.nodeB.toStringTree(); } return null; } /** * Compare this node to node2 recursively and return the first pair of nodes * that differs doing a preorder depth-first traversal. Package private for * testing. Returns null if the nodes are equivalent. */ NodeMismatch checkTreeEqualsImpl(Node node2) { if (!isEquivalentTo(node2, false, false)) { return new NodeMismatch(this, node2); } NodeMismatch res = null; Node n, n2; for (n = first, n2 = node2.first; res == null && n != null; n = n.next, n2 = n2.next) { if (node2 == null) { throw new IllegalStateException(); } res = n.checkTreeEqualsImpl(n2); if (res != null) { return res; } } return res; } /** * Compare this node to node2 recursively and return the first pair of nodes * that differs doing a preorder depth-first traversal. Package private for * testing. Returns null if the nodes are equivalent. */ NodeMismatch checkTreeTypeAwareEqualsImpl(Node node2) { // Do a non-recursive equivalents check. if (!isEquivalentTo(node2, true, false)) { return new NodeMismatch(this, node2); } NodeMismatch res = null;

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> Node n, n2; for (n = first, n2 = node2.first; res == null && n != null; n = n.next, n2 = n2.next) { res = n.checkTreeTypeAwareEqualsImpl(n2); if (res != null) { return res; } } return res; } public static String tokenToName(int token) { switch (token) { case Token.ERROR: return "error"; case Token.RETURN: return "return"; case Token.BITOR: return "bitor"; case Token.BITXOR: return "bitxor"; case Token.BITAND: return "bitand"; case Token.EQ: return "eq"; case Token.NE: return "ne"; case Token.LT: return "lt"; case Token.LE: return "le"; case Token.GT: return "gt"; case Token.GE: return "ge"; case Token.LSH: return "lsh"; case Token.RSH: return "rsh"; case Token.URSH: return "ursh"; case Token.ADD: return "add"; case Token.SUB: return "sub"; case Token.MUL: return "mul"; case Token.DIV: return "div"; case Token.MOD: return "mod"; case Token.BITNOT: return "bitnot"; case Token.NEG: return "neg"; case Token.NEW: return "new"; case Token.DELPROP: return "delprop"; case Token.TYPEOF: return "typeof"; case Token.GETPROP: return "getprop"; case Token.GETELEM: return "getelem"; case Token.CALL: return "call"; case Token.NAME: return "name"; case Token.NUMBER: return "number"; case Token.STRING: return "string"; case Token.NULL: return "null"; case Token.THIS: return "this"; case Token.FALSE: return "false"; case Token.TRUE: return "true"; case Token.SHEQ: return "sheq"; case Token.SHNE: return "shne"; case Token.REGEXP: return "regexp"; case Token.POS: return "pos"; case Token.THROW: return "throw"; case Token.IN: return "in"; case Token.INSTANCEOF: return "instanceof"; case Token.TRY: return "try"; case Token.PARAM_LIST: return "lp"; case Token.COMMA: return "comma"; case Token.ASSIGN: return "assign"; case Token.ASSIGN_BITOR: return "assign_b

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> boolean isEquivalentTo(Node node, boolean compareJsType, boolean recurse) { if (type != node.getType() || getChildCount() != node.getChildCount() || this.getClass() != node.getClass()) { return false; } if (compareJsType && !JSType.isEquivalent(jsType, node.getJSType())) { return false; } if (type == Token.INC || type == Token.DEC) { int post1 = this.getIntProp(INCRDECR_PROP); int post2 = node.getIntProp(INCRDECR_PROP); if (post1 != post2) { return false; } } else if (type == Token.STRING) { int quoted1 = this.getIntProp(QUOTED_PROP); int quoted2 = node.getIntProp(QUOTED_PROP); if (quoted1 != quoted2) { return false; } } else if (type == Token.CALL) { if (this.getBooleanProp(FREE_CALL) != node.getBooleanProp(FREE_CALL)) { return false; } } if (recurse) { Node n, n2; for (n = first, n2 = node.first; n != null; n = n.next, n2 = n2.next) { if (!n.isEquivalentTo(n2, compareJsType, true)) { return false; } } } return true; } /** * This function takes a set of GETPROP nodes and produces a string that is * each property separated by dots. If the node ultimately under the left * sub-tree is not a simple name, this is not a valid qualified name. * * @return a null if this is not a qualified name, or a dot-separated string * of the name and properties. */ public String getQualifiedName() { if (type == Token.NAME) { return getString(); } else if (type == Token.GETPROP) { String left = getFirstChild().getQualifiedName(); if (left == null) { return null; } return left + "." + getLastChild().getString(); } else if (type == Token.THIS) { return "this"; } else { return null; } } /** * Returns whether a node corresponds to a simple or a qualified name, such as * <code>x</code> or <code>a.b.c</code> or <code>this.a</code>. */ public boolean isQualifiedName() { switch (getType()) { case Token.NAME: case Token.THIS: return true; case Token.GETPROP: return getFirstChild().isQualifiedName(); default: return false; } } /** * Returns whether a node corresponds to a simple

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> or a qualified name without * a "this" reference, such as <code>a.b.c</code>, but not <code>this.a</code> * . */ public boolean isUnscopedQualifiedName() { switch (getType()) { case Token.NAME: return true; case Token.GETPROP: return getFirstChild().isUnscopedQualifiedName(); default: return false; } } // ========================================================================== // Mutators /** * Removes this node from its parent. Equivalent to: * node.getParent().removeChild(); */ public Node detachFromParent() { Preconditions.checkState(parent != null); parent.removeChild(this); return this; } /** * Removes the first child of Node. Equivalent to: * node.removeChild(node.getFirstChild()); * * @return The removed Node. */ public Node removeFirstChild() { Node child = first; if (child != null) { removeChild(child); } return child; } /** * @return A Node that is the head of the list of children. */ public Node removeChildren() { Node children = first; for (Node child = first; child != null; child = child.getNext()) { child.parent = null; } first = null; last = null; return children; } /** * Removes all children from this node and isolates the children from each * other. */ public void detachChildren() { for (Node child = first; child != null;) { Node nextChild = child.getNext(); child.parent = null; child.next = null; child = nextChild; } first = null; last = null; } public Node removeChildAfter(Node prev) { Preconditions.checkArgument(prev.parent == this, "prev is not a child of this node."); Preconditions.checkArgument(prev.next != null, "no next sibling."); Node child = prev.next; prev.next = child.next; if (child == last) last = prev; child.next = null; child.parent = null; return child; } /** * @return A detached clone of the Node, specifically excluding its children. */ public Node cloneNode() { Node result; try { result = (Node) super.clone(); // PropListItem lists are immutable and can be shared so there is no // need to clone them here. result.next = null; result.first = null; result.last = null; result.parent = null; } catch (CloneNotSupportedException e) { throw new RuntimeException(e.getMessage()); } return result; } /** * @return A detached clone of the Node and all its children. */ public

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>/* * Copyright 2008 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.collect.Maps; import com.google.javascript.jscomp.CheckLevel; import com.google.javascript.jscomp.NodeTraversal.AbstractShallowCallback; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.JSDocInfo.Visibility; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.Map; /** * Insures '@constructor X' has a 'goog.provide("X")' . * */ class CheckProvides implements HotSwapCompilerPass { private final AbstractCompiler compiler; private final CheckLevel checkLevel; private final CodingConvention codingConvention; static final DiagnosticType MISSING_PROVIDE_WARNING = DiagnosticType.disabled( "JSC_MISSING_PROVIDE", "missing goog.provide(''{0}'')"); CheckProvides(AbstractCompiler compiler, CheckLevel checkLevel) { this.compiler = compiler; this.checkLevel = checkLevel; this.codingConvention = compiler.getCodingConvention(); } @Override public void process(Node externs, Node root) { hotSwapScript(root, null); } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { CheckProvidesCallback callback = new CheckProvidesCallback(codingConvention); new NodeTraversal(compiler, callback).traverse(scriptRoot); } private class CheckProvidesCallback extends AbstractShallowCallback { private final Map<String, Node> provides = Maps.newHashMap(); private final Map<String, Node> ctors = Maps.newHashMap(); private final CodingConvention convention; CheckProvidesCallback(CodingConvention convention){ this.convention = convention; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.CALL: String providedClassName = codingConvention.extractClassNameIfProvide(n, parent); if (providedClassName != null) { provides.put(providedClassName, n); } break; case Token

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>.FUNCTION: visitFunctionNode(n, parent); break; case Token.SCRIPT: visitScriptNode(t, n); } } private void visitFunctionNode(Node n, Node parent) { Node name = null; JSDocInfo info = parent.getJSDocInfo(); if (info != null && info.isConstructor()) { name = parent.getFirstChild(); } else { // look to the child, maybe it's a named function info = n.getJSDocInfo(); if (info != null && info.isConstructor()) { name = n.getFirstChild(); } } if (name != null && name.isQualifiedName()) { String qualifiedName = name.getQualifiedName(); if (!this.convention.isPrivate(qualifiedName)) { Visibility visibility = info.getVisibility(); if (!visibility.equals(JSDocInfo.Visibility.PRIVATE)) { ctors.put(qualifiedName, name); } } } } private void visitScriptNode(NodeTraversal t, Node n) { for (Map.Entry<String, Node> ctorEntry : ctors.entrySet()) { if (!provides.containsKey(ctorEntry.getKey())) { compiler.report( t.makeError(ctorEntry.getValue(), checkLevel, MISSING_PROVIDE_WARNING, ctorEntry.getKey())); } } provides.clear(); ctors.clear(); } } }

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> : number\n" + "required: E.<number>"); } public void testEnum20() throws Exception { testTypes("/**@enum*/ var E = {A: 1, B: 2}; var x = []; x[E.A] = 0;"); } public void testEnum21() throws Exception { Node n = parseAndTypeCheck( "/** @enum {string} */ var E = {A : 'a', B : 'b'};\n" + "/** @param {!E} x\n@return {!E} */ function f(x) { return x; }"); Node nodeX = n.getLastChild().getLastChild().getLastChild().getLastChild(); JSType typeE = nodeX.getJSType(); assertFalse(typeE.isObject()); assertFalse(typeE.isNullable()); } public void testEnum22() throws Exception { testTypes("/**@enum*/ var E = {A: 1, B: 2};" + "/** @param {E} x \n* @return {number} */ function f(x) {return x}"); } public void testEnum23() throws Exception { testTypes("/**@enum*/ var E = {A: 1, B: 2};" + "/** @param {E} x \n* @return {string} */ function f(x) {return x}", "inconsistent return type\n" + "found : E.<number>\n" + "required: string"); } public void testEnum24() throws Exception { testTypes("/**@enum {Object} */ var E = {A: {}};" + "/** @param {E} x \n* @return {!Object} */ function f(x) {return x}", "inconsistent return type\n" + "found : E.<(Object|null)>\n" + "required: Object"); } public void testEnum25() throws Exception { testTypes("/**@enum {!Object} */ var E = {A: {}};" + "/** @param {E} x \n* @return {!Object} */ function f(x) {return x}"); } public void testEnum26() throws Exception { testTypes("var a = {}; /**@enum*/ a.B = {A: 1, B: 2};" + "/** @param {a.B} x \n* @return {number} */ function f(x) {return x}"); } public void testEnum27() throws Exception { // x is unknown testTypes("/** @enum */ var A = {B: 1, C: 2}; " + "function f(x) { return A == x; }"); } public void testEnum28() throws Exception {

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> of g does not match formal parameter\n" + "found : Error\n" + "required: Array"); } public void testBackwardsTypedefUse10() throws Exception { testTypes( "/** @param {goog.MyEnum} x */ function g(x) {}" + "var goog = {};" + "/** @enum {goog.MyTypedef} */ goog.MyEnum = {FOO: 1};" + "/** @typedef {number} */ goog.MyTypedef;" + "g(1);", "actual parameter 1 of g does not match formal parameter\n" + "found : number\n" + "required: goog.MyEnum.<number>"); } public void testBackwardsConstructor1() throws Exception { testTypes( "function f() { (new Foo(true)); }" + "/** \n * @constructor \n * @param {number} x */" + "var Foo = function(x) {};", "actual parameter 1 of Foo does not match formal parameter\n" + "found : boolean\n" + "required: number"); } public void testBackwardsConstructor2() throws Exception { testTypes( "function f() { (new Foo(true)); }" + "/** \n * @constructor \n * @param {number} x */" + "var YourFoo = function(x) {};" + "/** \n * @constructor \n * @param {number} x */" + "var Foo = YourFoo;", "actual parameter 1 of Foo does not match formal parameter\n" + "found : boolean\n" + "required: number"); } public void testMinimalConstructorAnnotation() throws Exception { testTypes("/** @constructor */function Foo(){}"); } public void testGoodExtends1() throws Exception { // A minimal @extends example testTypes("/** @constructor */function base() {}\n" + "/** @constructor\n * @extends {base} */function derived() {}\n"); } public void testGoodExtends2() throws Exception { testTypes("/** @constructor\n * @extends base */function derived() {}\n" + "/** @constructor */function base() {}\n"); } public void testGoodExtends3() throws Exception { testTypes("/** @constructor\n * @extends {Object} */function base() {}\n" + "/** @constructor\n * @extends {base} */function derived() {}\n"); } public void testGoodExtends4() throws Exception { // Ensure that @extends actually sets the base type of a constructor // correctly. Because this isn't part of the human-readable Function // definition, we need to crawl the prototype chain (eww). Node n = parseAndTypeCheck( "var goog

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> = {};\n" + "/** @constructor */goog.Base = function(){};\n" + "/** @constructor\n" + " * @extends {goog.Base} */goog.Derived = function(){};\n"); Node subTypeName = n.getLastChild().getLastChild().getFirstChild(); assertEquals("goog.Derived", subTypeName.getQualifiedName()); FunctionType subCtorType = (FunctionType) subTypeName.getNext().getJSType(); assertEquals("goog.Derived", subCtorType.getInstanceType().toString()); JSType superType = subCtorType.getPrototype().getImplicitPrototype(); assertEquals("goog.Base", superType.toString()); } public void testGoodExtends5() throws Exception { // we allow for the extends annotation to be placed first testTypes("/** @constructor */function base() {}\n" + "/** @extends {base}\n * @constructor */function derived() {}\n"); } public void testGoodExtends6() throws Exception { testFunctionType( CLOSURE_DEFS + "/** @constructor */function base() {}\n" + "/** @return {number} */ " + " base.prototype.foo = function() { return 1; };\n" + "/** @extends {base}\n * @constructor */function derived() {}\n" + "goog.inherits(derived, base);", "derived.superClass_.foo", "function (this:base): number"); } public void testGoodExtends7() throws Exception { testFunctionType( "Function.prototype.inherits = function(x) {};" + "/** @constructor */function base() {}\n" + "/** @extends {base}\n * @constructor */function derived() {}\n" + "derived.inherits(base);", "(new derived).constructor", "function (new:derived): undefined"); } public void testGoodExtends8() throws Exception { testTypes("/** @constructor \n @extends {Base} */ function Sub() {}" + "/** @return {number} */ function f() { return (new Sub()).foo; }" + "/** @constructor */ function Base() {}" + "/** @type {boolean} */ Base.prototype.foo = true;", "inconsistent return type\n" + "found : boolean\n" + "required: number"); } public void testGoodExtends9() throws Exception { testTypes( "/** @constructor */ function Super() {}" + "Super.prototype.foo = function() {};" + "/** @constructor \n * @extends {Super} */ function Sub() {}" + "Sub.prototype = new Super();" + "/** @override */ Sub.prototype.foo = function() {};"); } public void testGoodExtends10() throws Exception { testTypes(

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> } public void testImplicitCast() throws Exception { testTypes("/** @constructor */ function Element() {};\n" + "/** @type {string}\n" + " * @implicitCast */" + "Element.prototype.innerHTML;", "(new Element).innerHTML = new Array();", null, false); } public void testImplicitCastSubclassAccess() throws Exception { testTypes("/** @constructor */ function Element() {};\n" + "/** @type {string}\n" + " * @implicitCast */" + "Element.prototype.innerHTML;" + "/** @constructor \n @extends Element */" + "function DIVElement() {};", "(new DIVElement).innerHTML = new Array();", null, false); } public void testImplicitCastNotInExterns() throws Exception { testTypes("/** @constructor */ function Element() {};\n" + "/** @type {string}\n" + " * @implicitCast */" + "Element.prototype.innerHTML;" + "(new Element).innerHTML = new Array();", new String[] { "Illegal annotation on innerHTML. @implicitCast may only be " + "used in externs.", "assignment to property innerHTML of Element\n" + "found : Array\n" + "required: string"}); } public void testNumberNode() throws Exception { Node n = typeCheck(Node.newNumber(0)); assertEquals(NUMBER_TYPE, n.getJSType()); } public void testStringNode() throws Exception { Node n = typeCheck(Node.newString("hello")); assertEquals(STRING_TYPE, n.getJSType()); } public void testBooleanNodeTrue() throws Exception { Node trueNode = typeCheck(new Node(Token.TRUE)); assertEquals(BOOLEAN_TYPE, trueNode.getJSType()); } public void testBooleanNodeFalse() throws Exception { Node falseNode = typeCheck(new Node(Token.FALSE)); assertEquals(BOOLEAN_TYPE, falseNode.getJSType()); } public void testUndefinedNode() throws Exception { Node p = new Node(Token.ADD); Node n = Node.newString(Token.NAME, "undefined"); p.addChildToBack(n); p.addChildToBack(Node.newNumber(5)); typeCheck(p); assertEquals(VOID_TYPE, n.getJSType()); } public void testNumberAutoboxing() throws Exception { testTypes("/** @type Number */var a = 4;", "initializing variable\n" + "found : number\n" + "required: (Number|null)"); } public void testNumberUnboxing() throws Exception { testTypes("/** @type number */var a = new Number(4);", "initializing variable\n" +

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> "actual parameter 1 of ns.ns2.Foo does not match formal parameter\n" + "found : boolean\n" + "required: number"); } public void testQualifiedNameInference10() throws Exception { testTypes( "var ns = {}; " + "ns.ns2 = {}; " + "(function() { " + " /** @interface */ " + " ns.ns2.Foo = function() {};" + " /** @constructor \n * @implements {ns.ns2.Foo} */ " + " function F() {}" + " (new F());" + "})();"); } public void testSheqRefinedScope() throws Exception { Node n = parseAndTypeCheck( "/** @constructor */function A() {}\n" + "/** @constructor \n @extends A */ function B() {}\n" + "/** @return {number} */\n" + "B.prototype.p = function() { return 1; }\n" + "/** @param {A} a\n @param {B} b */\n" + "function f(a, b) {\n" + " b.p();\n" + " if (a === b) {\n" + " b.p();\n" + " }\n" + "}"); Node nodeC = n.getLastChild().getLastChild().getLastChild().getLastChild() .getLastChild().getLastChild(); JSType typeC = nodeC.getJSType(); assertTrue(typeC.isNumber()); Node nodeB = nodeC.getFirstChild().getFirstChild(); JSType typeB = nodeB.getJSType(); assertEquals("B", typeB.toString()); } public void testAssignToUntypedVariable() throws Exception { Node n = parseAndTypeCheck("var z; z = 1;"); Node assign = n.getLastChild().getFirstChild(); Node node = assign.getFirstChild(); assertFalse(node.getJSType().isUnknownType()); assertEquals("number", node.getJSType().toString()); } public void testAssignToUntypedProperty() throws Exception { Node n = parseAndTypeCheck( "/** @constructor */ function Foo() {}\n" + "Foo.prototype.a = 1;" + "(new Foo).a;"); Node node = n.getLastChild().getFirstChild(); assertFalse(node.getJSType().isUnknownType()); assertTrue(node.getJSType().isNumber()); } public void testNew1() throws Exception { testTypes("new 4", TypeCheck.NOT_A_CONSTRUCTOR); } public void testNew2() throws Exception { testTypes("var Math = {}; new Math()", TypeCheck.NOT_A_CONSTRUCTOR); } public void testNew

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> + "* @param {number} start\n" + "* @param {number} opt_length\n" + "* @return {string}\n" + "*/\n" + "String.prototype.substr = function(start, opt_length) {};\n"; Node n2 = parseAndTypeCheck(externs + "\"x\".substr(0,1);"); assertEquals(STRING_TYPE, n2.getLastChild().getFirstChild().getJSType()); } public void testExtendFunction1() throws Exception { Node n = parseAndTypeCheck("/**@return {number}*/Function.prototype.f = " + "function() { return 1; };\n" + "(new Function()).f();"); JSType type = n.getLastChild().getLastChild().getJSType(); assertEquals(NUMBER_TYPE, type); } public void testExtendFunction2() throws Exception { Node n = parseAndTypeCheck("/**@return {number}*/Function.prototype.f = " + "function() { return 1; };\n" + "(function() {}).f();"); JSType type = n.getLastChild().getLastChild().getJSType(); assertEquals(NUMBER_TYPE, type); } public void testInheritanceCheck1() throws Exception { testTypes( "/** @constructor */function Super() {};" + "/** @constructor\n @extends {Super} */function Sub() {};" + "Sub.prototype.foo = function() {};"); } public void testInheritanceCheck2() throws Exception { testTypes( "/** @constructor */function Super() {};" + "/** @constructor\n @extends {Super} */function Sub() {};" + "/** @override */Sub.prototype.foo = function() {};", "property foo not defined on any superclass of Sub"); } public void testInheritanceCheck3() throws Exception { testTypes( "/** @constructor */function Super() {};" + "Super.prototype.foo = function() {};" + "/** @constructor\n @extends {Super} */function Sub() {};" + "Sub.prototype.foo = function() {};", "property foo already defined on superclass Super; " + "use @override to override it"); } public void testInheritanceCheck4() throws Exception { testTypes( "/** @constructor */function Super() {};" + "Super.prototype.foo = function() {};" + "/** @constructor\n @extends {Super} */function Sub() {};" + "/** @override */Sub.prototype.foo = function() {};"); } public void testInheritanceCheck5() throws Exception { testTypes( "/** @constructor */function Root() {};" + "Root.prototype.foo = function() {};" + "/** @constructor\n @extends {Root} */function Super() {};"

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>/** @desc description */Int.prototype.foo = function() {};" + "/** @interface \n @extends {Int} */function Int2() {};" + "/** @constructor\n @implements {Int2} */function Foo() {};", "property foo on interface Int is not implemented by type Foo"); } public void testStubConstructorImplementingInterface() throws Exception { // This does not throw a warning for unimplemented property because Foo is // just a stub. testTypes( // externs "/** @interface */ function Int() {}\n" + "/** @desc description */Int.prototype.foo = function() {};" + "/** @constructor \n @implements {Int} */ var Foo;\n", "", null, false); } public void testObjectLiteral() throws Exception { Node n = parseAndTypeCheck("var a = {m1: 7, m2: 'hello'}"); Node nameNode = n.getFirstChild().getFirstChild(); Node objectNode = nameNode.getFirstChild(); // node extraction assertEquals(Token.NAME, nameNode.getType()); assertEquals(Token.OBJECTLIT, objectNode.getType()); // value's type ObjectType objectType = (ObjectType) objectNode.getJSType(); assertEquals(NUMBER_TYPE, objectType.getPropertyType("m1")); assertEquals(STRING_TYPE, objectType.getPropertyType("m2")); // variable's type assertEquals(objectType, nameNode.getJSType()); } public void testObjectLiteralDeclaration1() throws Exception { testTypes( "var x = {" + "/** @type {boolean} */ abc: true," + "/** @type {number} */ 'def': 0," + "/** @type {string} */ 3: 'fgh'" + "};"); } public void testObjectLiteralDeclaration2() throws Exception { testTypes( "var x = {" + " /** @type {boolean} */ abc: true" + "};" + "x.abc = 0;", "assignment to property abc of x\n" + "found : number\n" + "required: boolean"); } public void testObjectLiteralDeclaration3() throws Exception { testTypes( "/** @param {{foo: !Function}} x */ function f(x) {}" + "f({foo: function() {}});"); } public void testObjectLiteralDeclaration4() throws Exception { testClosureTypes( "var x = {" + " /** @param {boolean} x */ abc: function(x) {}" + "};" + "/**\n" + " * @param {string} x\n" + " * @suppress {duplicate}\n" + " */ x.abc = function(x) {};", "assignment to property abc of x\n

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>" + "found : function (string): undefined\n" + "required: function (boolean): undefined"); // TODO(user): suppress {duplicate} currently also silence the // redefining type error in the TypeValidator. May be it needs // a new suppress name instead? } public void testObjectLiteralDeclaration5() throws Exception { testTypes( "var x = {" + " /** @param {boolean} x */ abc: function(x) {}" + "};" + "/**\n" + " * @param {boolean} x\n" + " * @suppress {duplicate}\n" + " */ x.abc = function(x) {};"); } public void testObjectLiteralDeclaration6() throws Exception { testTypes( "var x = {};" + "/**\n" + " * @param {boolean} x\n" + " * @suppress {duplicate}\n" + " */ x.abc = function(x) {};" + "x = {" + " /**\n" + " * @param {boolean} x\n" + " * @suppress {duplicate}\n" + " */" + " abc: function(x) {}" + "};"); } public void testObjectLiteralDeclaration7() throws Exception { testTypes( "var x = {};" + "/**\n" + " * @type {function(boolean): undefined}\n" + " */ x.abc = function(x) {};" + "x = {" + " /**\n" + " * @param {boolean} x\n" + " * @suppress {duplicate}\n" + " */" + " abc: function(x) {}" + "};"); } public void testCallDateConstructorAsFunction() throws Exception { // ECMA-262 15.9.2: When Date is called as a function rather than as a // constructor, it returns a string. Node n = parseAndTypeCheck("Date()"); assertEquals(STRING_TYPE, n.getFirstChild().getFirstChild().getJSType()); } // According to ECMA-262, Error & Array function calls are equivalent to // constructor calls. public void testCallErrorConstructorAsFunction() throws Exception { Node n = parseAndTypeCheck("Error('x')"); assertEquals(ERROR_TYPE, n.getFirstChild().getFirstChild().getJSType()); } public void testCallArrayConstructorAsFunction() throws Exception { Node n = parseAndTypeCheck("Array()"); assertEquals(ARRAY_TYPE, n.getFirstChild().getFirstChild().getJSType()); } public void testPropertyTypeOfUnionType() throws Exception { testTypes("var a = {};" + "

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> public void testResolutionViaRegistry4() throws Exception { testTypes("/** @constructor */ u.A = function() {};\n" + "/**\n* @constructor\n* @extends {u.A}\n*/\nu.A.A = function() {}\n;" + "/**\n* @constructor\n* @extends {u.A}\n*/\nu.A.B = function() {};\n" + "var ab = new u.A.B();\n" + "/** @type {!u.A} */ var a = ab;\n" + "/** @type {!u.A.A} */ var aa = ab;\n", "initializing variable\n" + "found : u.A.B\n" + "required: u.A.A"); } public void testResolutionViaRegistry5() throws Exception { Node n = parseAndTypeCheck("/** @constructor */ u.T = function() {}; u.T"); JSType type = n.getLastChild().getLastChild().getJSType(); assertFalse(type.isUnknownType()); assertTrue(type instanceof FunctionType); assertEquals("u.T", ((FunctionType) type).getInstanceType().getReferenceName()); } public void testGatherProperyWithoutAnnotation1() throws Exception { Node n = parseAndTypeCheck("/** @constructor */ var T = function() {};" + "/** @type {!T} */var t; t.x; t;"); JSType type = n.getLastChild().getLastChild().getJSType(); assertFalse(type.isUnknownType()); assertTrue(type instanceof ObjectType); ObjectType objectType = (ObjectType) type; assertFalse(objectType.hasProperty("x")); assertEquals( Lists.newArrayList(objectType), registry.getTypesWithProperty("x")); } public void testGatherProperyWithoutAnnotation2() throws Exception { TypeCheckResult ns = parseAndTypeCheckWithScope("/** @type {!Object} */var t; t.x; t;"); Node n = ns.root; Scope s = ns.scope; JSType type = n.getLastChild().getLastChild().getJSType(); assertFalse(type.isUnknownType()); assertEquals(type, OBJECT_TYPE); assertTrue(type instanceof ObjectType); ObjectType objectType = (ObjectType) type; assertFalse(objectType.hasProperty("x")); assertEquals( Lists.newArrayList(OBJECT_TYPE), registry.getTypesWithProperty("x")); } public void testFunctionMasksVariableBug() throws Exception { testTypes("var x = 4; var f = function x(b) { return b ? 1 : x(true); };", "function x masks variable (IE bug)"); } public void testDfa1() throws Exception { testTypes("var x = null;\n x = 1;\n /** @

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>x) { x.a = x.b; }"; assertEquals(50.0, getTypedPercent(js), 0.1); } public void testGetTypedPercent4() throws Exception { String js = "var n = {};\n /** @constructor */ n.T = function() {};\n" + "/** @type n.T */ var x = new n.T();"; assertEquals(100.0, getTypedPercent(js), 0.1); } public void testGetTypedPercent5() throws Exception { String js = "/** @enum {number} */ keys = {A: 1,B: 2,C: 3};"; assertEquals(100.0, getTypedPercent(js), 0.1); } public void testGetTypedPercent6() throws Exception { String js = "a = {TRUE: 1, FALSE: 0};"; assertEquals(100.0, getTypedPercent(js), 0.1); } private double getTypedPercent(String js) throws Exception { Node n = compiler.parseTestCode(js); Node externs = new Node(Token.BLOCK); Node externAndJsRoot = new Node(Token.BLOCK, externs, n); externAndJsRoot.setIsSyntheticBlock(true); TypeCheck t = makeTypeCheck(); t.processForTesting(null, n); return t.getTypedPercent(); } private ObjectType getInstanceType(Node js1Node) { JSType type = js1Node.getFirstChild().getJSType(); assertNotNull(type); assertTrue(type instanceof FunctionType); FunctionType functionType = (FunctionType) type; assertTrue(functionType.isConstructor()); return functionType.getInstanceType(); } public void testPrototypePropertyReference() throws Exception { TypeCheckResult p = parseAndTypeCheckWithScope("" + "/** @constructor */\n" + "function Foo() {}\n" + "/** @param {number} a */\n" + "Foo.prototype.bar = function(a){};\n" + "/** @param {Foo} f */\n" + "function baz(f) {\n" + " Foo.prototype.bar.call(f, 3);\n" + "}"); assertEquals(0, compiler.getErrorCount()); assertEquals(0, compiler.getWarningCount()); assertTrue(p.scope.getVar("Foo").getType() instanceof FunctionType); FunctionType fooType = (FunctionType) p.scope.getVar("Foo").getType(); assertEquals("function (this:Foo, number): undefined", fooType.getPrototype().getPropertyType("bar").toString()); } public void testResolvingNamedTypes() throws Exception { String js = "" + "/** @constructor */\n" + "var Foo = function() {}\n"

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>Foo.prototype} */ ({bar: 1}));" + "alert((new Foo()).bar);"); } public void testLends7() throws Exception { testTypes( "function extend(x, y) {}" + "/** @constructor */ function Foo() {}" + "extend(Foo, /** @lends {Foo.prototype|Foo} */ ({bar: 1}));", "Bad type annotation. expected closing }"); } public void testLends8() throws Exception { testTypes( "function extend(x, y) {}" + "/** @type {number} */ var Foo = 3;" + "extend(Foo, /** @lends {Foo} */ ({bar: 1}));", "May only lend properties to object types. Foo has type number."); } public void testLends9() throws Exception { testClosureTypesMultipleWarnings( "function extend(x, y) {}" + "/** @constructor */ function Foo() {}" + "extend(Foo, /** @lends {!Foo} */ ({bar: 1}));", Lists.newArrayList( "Bad type annotation. expected closing }", "Bad type annotation. missing object name in @lends tag")); } public void testDeclaredNativeTypeEquality() throws Exception { Node n = parseAndTypeCheck("/** @constructor */ function Object() {};"); assertEquals(registry.getNativeType(JSTypeNative.OBJECT_FUNCTION_TYPE), n.getFirstChild().getJSType()); } public void testUndefinedVar() throws Exception { Node n = parseAndTypeCheck("var undefined;"); assertEquals(registry.getNativeType(JSTypeNative.VOID_TYPE), n.getFirstChild().getFirstChild().getJSType()); } public void testFlowScopeBug1() throws Exception { Node n = parseAndTypeCheck("/** @param {number} a \n" + "* @param {number} b */\n" + "function f(a, b) {\n" + "/** @type number */" + "var i = 0;" + "for (; (i + a) < b; ++i) {}}"); // check the type of the add node for i + f assertEquals(registry.getNativeType(JSTypeNative.NUMBER_TYPE), n.getFirstChild().getLastChild().getLastChild().getFirstChild() .getNext().getFirstChild().getJSType()); } public void testFlowScopeBug2() throws Exception { Node n = parseAndTypeCheck("/** @constructor */ function Foo() {};\n" + "Foo.prototype.hi = false;" + "function foo(a, b) {\n" + " /** @type Array */" + " var arr;" + " /** @type number */" + " var iter;" + " for (iter =

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> 0; iter < arr.length; ++ iter) {" + " /** @type Foo */" + " var afoo = arr[iter];" + " afoo;" + " }" + "}"); // check the type of afoo when referenced assertEquals(registry.createNullableType(registry.getType("Foo")), n.getLastChild().getLastChild().getLastChild().getLastChild() .getLastChild().getLastChild().getJSType()); } public void testAddSingletonGetter() { Node n = parseAndTypeCheck( "/** @constructor */ function Foo() {};\n" + "goog.addSingletonGetter(Foo);"); ObjectType o = (ObjectType) n.getFirstChild().getJSType(); assertEquals("function (): Foo", o.getPropertyType("getInstance").toString()); assertEquals("Foo", o.getPropertyType("instance_").toString()); } public void testTypeCheckStandaloneAST() throws Exception { Node n = compiler.parseTestCode("function Foo() { }"); typeCheck(n); TypedScopeCreator scopeCreator = new TypedScopeCreator(compiler); Scope topScope = scopeCreator.createScope(n, null); Node second = compiler.parseTestCode("new Foo"); Node externs = new Node(Token.BLOCK); Node externAndJsRoot = new Node(Token.BLOCK, externs, second); externAndJsRoot.setIsSyntheticBlock(true); new TypeCheck( compiler, new SemanticReverseAbstractInterpreter( compiler.getCodingConvention(), registry), registry, topScope, scopeCreator, CheckLevel.WARNING, CheckLevel.OFF) .process(null, second); assertEquals(1, compiler.getWarningCount()); assertEquals("cannot instantiate non-constructor", compiler.getWarnings()[0].description); } public void testUpdateParameterTypeOnClosure() throws Exception { testTypes( "/**\n" + "* @constructor\n" + "* @param {*=} opt_value\n" + "* @return {?}\n" + "*/\n" + "function Object(opt_value) {}\n" + "/**\n" + "* @constructor\n" + "* @param {...*} var_args\n" + "*/\n" + "function Function(var_args) {}\n" + "/**\n" + "* @type {Function}\n" + "*/\n" + // The line below sets JSDocInfo on Object so that the type of the // argument to function f has JSDoc through its prototype chain. "Object.prototype.constructor = function() {};\n", "/**\n" + "* @param {function(): boolean} fn\n" + "*/\n" + "function f(fn) {}\n" + "f(function(g

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> String js, List<String> descriptions) throws Exception { Node n = compiler.parseTestCode(js); Node externs = new Node(Token.BLOCK); Node externAndJsRoot = new Node(Token.BLOCK, externs, n); externAndJsRoot.setIsSyntheticBlock(true); assertEquals("parsing error: " + Joiner.on(", ").join(compiler.getErrors()), 0, compiler.getErrorCount()); // For processing goog.addDependency for forward typedefs. new ProcessClosurePrimitives(compiler, null, CheckLevel.ERROR, true) .process(null, n); CodingConvention convention = compiler.getCodingConvention(); new TypeCheck(compiler, new ClosureReverseAbstractInterpreter( convention, registry).append( new SemanticReverseAbstractInterpreter( convention, registry)) .getFirst(), registry) .processForTesting(null, n); assertEquals( "unexpected error(s) : " + Joiner.on(", ").join(compiler.getErrors()), 0, compiler.getErrorCount()); if (descriptions == null) { assertEquals( "unexpected warning(s) : " + Joiner.on(", ").join(compiler.getWarnings()), 0, compiler.getWarningCount()); } else { assertEquals( "unexpected warning(s) : " + Joiner.on(", ").join(compiler.getWarnings()), descriptions.size(), compiler.getWarningCount()); for (int i = 0; i < descriptions.size(); i++) { assertEquals(descriptions.get(i), compiler.getWarnings()[i].description); } } } void testTypes(String js, String description, boolean isError) throws Exception { testTypes(DEFAULT_EXTERNS, js, description, isError); } void testTypes(String externs, String js, String description, boolean isError) throws Exception { Node n = parseAndTypeCheck(externs, js); JSError[] errors = compiler.getErrors(); if (description != null && isError) { assertTrue("expected an error", errors.length > 0); assertEquals(description, errors[0].description); errors = Arrays.asList(errors).subList(1, errors.length).toArray( new JSError[errors.length - 1]); } if (errors.length > 0) { fail("unexpected error(s):\n" + Joiner.on("\n").join(errors)); } JSError[] warnings = compiler.getWarnings(); if (description != null && !isError) { assertTrue("expected a warning", warnings.length > 0); assertEquals(description, warnings[0].description); warnings = Arrays.asList(warnings).subList(1, warnings.length).toArray( new JSError[warnings.length - 1]); } if (warnings.length > 0

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>) { fail("unexpected warnings(s):\n" + Joiner.on("\n").join(warnings)); } } /** * Parses and type checks the JavaScript code. */ private Node parseAndTypeCheck(String js) { return parseAndTypeCheck(DEFAULT_EXTERNS, js); } private Node parseAndTypeCheck(String externs, String js) { return parseAndTypeCheckWithScope(externs, js).root; } /** * Parses and type checks the JavaScript code and returns the Scope used * whilst type checking. */ private TypeCheckResult parseAndTypeCheckWithScope(String js) { return parseAndTypeCheckWithScope(DEFAULT_EXTERNS, js); } private TypeCheckResult parseAndTypeCheckWithScope( String externs, String js) { compiler.init( Lists.newArrayList(JSSourceFile.fromCode("[externs]", externs)), Lists.newArrayList(JSSourceFile.fromCode("[testcode]", js)), compiler.getOptions()); Node n = compiler.getInput(new InputId("[testcode]")).getAstRoot(compiler); Node externsNode = compiler.getInput(new InputId("[externs]")) .getAstRoot(compiler); Node externAndJsRoot = new Node(Token.BLOCK, externsNode, n); externAndJsRoot.setIsSyntheticBlock(true); assertEquals("parsing error: " + Joiner.on(", ").join(compiler.getErrors()), 0, compiler.getErrorCount()); Scope s = makeTypeCheck().processForTesting(externsNode, n); return new TypeCheckResult(n, s); } private Node typeCheck(Node n) { Node externsNode = new Node(Token.BLOCK); Node externAndJsRoot = new Node(Token.BLOCK, externsNode, n); externAndJsRoot.setIsSyntheticBlock(true); makeTypeCheck().processForTesting(null, n); return n; } private TypeCheck makeTypeCheck() { return new TypeCheck( compiler, new SemanticReverseAbstractInterpreter( compiler.getCodingConvention(), registry), registry, reportMissingOverrides, CheckLevel.OFF); } void testTypes(String js, String[] warnings) throws Exception { Node n = compiler.parseTestCode(js); assertEquals(0, compiler.getErrorCount()); Node externsNode = new Node(Token.BLOCK); Node externAndJsRoot = new Node(Token.BLOCK, externsNode, n); makeTypeCheck().processForTesting(null, n); assertEquals(0, compiler.getErrorCount()); if (warnings != null) { assertEquals(warnings.length, compiler.getWarningCount()); JSError[] messages = compiler.getWarnings(); for (int i = 0; i < warnings.length && i < compiler

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>Position.put(null, ++astPositionCounter); // the implicit return is last. // Now, generate the priority of nodes by doing a depth-first // search on the CFG. priorityCounter = 0; DiGraphNode<Node, Branch> entry = cfg.getEntry(); prioritizeFromEntryNode(entry); if (shouldTraverseFunctions) { // If we're traversing inner functions, we need to rank the // priority of them too. for (DiGraphNode<Node, Branch> candidate : cfg.getDirectedGraphNodes()) { Node value = candidate.getValue(); if (value != null && value.isFunction()) { Preconditions.checkState( !nodePriorities.containsKey(candidate) || candidate == entry); prioritizeFromEntryNode(candidate); } } } // At this point, all reachable nodes have been given a priority, but // unreachable nodes have not been given a priority. Put them last. // Presumably, it doesn't really matter what priority they get, since // this shouldn't happen in real code. for (DiGraphNode<Node, Branch> candidate : cfg.getDirectedGraphNodes()) { if (!nodePriorities.containsKey(candidate)) { nodePriorities.put(candidate, ++priorityCounter); } } // Again, the implicit return node is always last. nodePriorities.put(cfg.getImplicitReturn(), ++priorityCounter); } /** * Given an entry node, find all the nodes reachable from that node * and prioritize them. */ private void prioritizeFromEntryNode(DiGraphNode<Node, Branch> entry) { PriorityQueue<DiGraphNode<Node, Branch>> worklist = new PriorityQueue<DiGraphNode<Node, Branch>>(10, priorityComparator); worklist.add(entry); while (!worklist.isEmpty()) { DiGraphNode<Node, Branch> current = worklist.remove(); if (nodePriorities.containsKey(current)) { continue; } nodePriorities.put(current, ++priorityCounter); List<DiGraphNode<Node, Branch>> successors = cfg.getDirectedSuccNodes(current); for (DiGraphNode<Node, Branch> candidate : successors) { worklist.add(candidate); } } } @Override public boolean shouldTraverse( NodeTraversal nodeTraversal, Node n, Node parent) { astPosition.put(n, astPositionCounter++); switch (n.getType()) { case Token.FUNCTION: if (shouldTraverseFunctions || n == cfg.getEntry().getValue()) { exceptionHandler.push(n); return true; } return false; case Token.TRY: exceptionHandler.push(n); return true; } /* * We are going to stop the traversal depending on what the node's

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> parent * is. * * We are only interested in adding edges between nodes that change control * flow. The most obvious ones are loops and IF-ELSE's. A statement * transfers control to its next sibling. * * In case of an expression tree, there is no control flow within the tree * even when there are short circuited operators and conditionals. When we * are doing data flow analysis, we will simply synthesize lattices up the * expression tree by finding the meet at each expression node. * * For example: within a Token.SWITCH, the expression in question does not * change the control flow and need not to be considered. */ if (parent != null) { switch (parent.getType()) { case Token.FOR: // Only traverse the body of the for loop. return n == parent.getLastChild(); // Skip the conditions. case Token.IF: case Token.WHILE: case Token.WITH: return n != parent.getFirstChild(); case Token.DO: return n != parent.getFirstChild().getNext(); // Only traverse the body of the cases case Token.SWITCH: case Token.CASE: case Token.CATCH: case Token.LABEL: return n != parent.getFirstChild(); case Token.FUNCTION: return n == parent.getFirstChild().getNext().getNext(); case Token.CONTINUE: case Token.BREAK: case Token.EXPR_RESULT: case Token.VAR: case Token.RETURN: case Token.THROW: return false; case Token.TRY: /* Just before we are about to visit the second child of the TRY node, * we know that we will be visiting either the CATCH or the FINALLY. * In other words, we know that the post order traversal of the TRY * block has been finished, no more exceptions can be caught by the * handler at this TRY block and should be taken out of the stack. */ if (n == parent.getFirstChild().getNext()) { Preconditions.checkState(exceptionHandler.peek() == parent); exceptionHandler.pop(); } } } return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.IF: handleIf(n); return; case Token.WHILE: handleWhile(n); return; case Token.DO: handleDo(n); return; case Token.FOR: handleFor(n); return; case Token.SWITCH: handleSwitch(n); return; case Token.CASE: handleCase(n); return; case Token.DEFAULT_CASE: handleDefault(n); return; case Token.BLOCK: case Token.SCRIPT: handleStmtList(n); return; case Token

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>.FUNCTION: handleFunction(n); return; case Token.EXPR_RESULT: handleExpr(n); return; case Token.THROW: handleThrow(n); return; case Token.TRY: handleTry(n); return; case Token.CATCH: handleCatch(n); return; case Token.BREAK: handleBreak(n); return; case Token.CONTINUE: handleContinue(n); return; case Token.RETURN: handleReturn(n); return; case Token.WITH: handleWith(n); return; case Token.LABEL: return; default: handleStmt(n); return; } } private void handleIf(Node node) { Node thenBlock = node.getFirstChild().getNext(); Node elseBlock = thenBlock.getNext(); createEdge(node, Branch.ON_TRUE, computeFallThrough(thenBlock)); if (elseBlock == null) { createEdge(node, Branch.ON_FALSE, computeFollowNode(node, this)); // not taken branch } else { createEdge(node, Branch.ON_FALSE, computeFallThrough(elseBlock)); } connectToPossibleExceptionHandler( node, NodeUtil.getConditionExpression(node)); } private void handleWhile(Node node) { // Control goes to the first statement if the condition evaluates to true. createEdge(node, Branch.ON_TRUE, computeFallThrough(node.getFirstChild().getNext())); // Control goes to the follow() if the condition evaluates to false. createEdge(node, Branch.ON_FALSE, computeFollowNode(node, this)); connectToPossibleExceptionHandler( node, NodeUtil.getConditionExpression(node)); } private void handleDo(Node node) { // The first edge can be the initial iteration as well as the iterations // after. createEdge(node, Branch.ON_TRUE, computeFallThrough(node.getFirstChild())); // The edge that leaves the do loop if the condition fails. createEdge(node, Branch.ON_FALSE, computeFollowNode(node, this)); connectToPossibleExceptionHandler( node, NodeUtil.getConditionExpression(node)); } private void handleFor(Node forNode) { if (forNode.getChildCount() == 4) { // We have for (init; cond; iter) { body } Node init = forNode.getFirstChild(); Node cond = init.getNext(); Node iter = cond.getNext(); Node body = iter.getNext(); // After initialization, we transfer to the FOR which is in charge of // checking the condition (for the first time). createEdge(init, Branch.UNCOND, forNode); // The edge that transfer control to the beginning of the loop body. createEdge(forNode, Branch.ON_TRUE, compute

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>getNext(); } if (nextSibling != null) { return computeFallThrough(nextSibling); } else { // If there are no more siblings, control is transfered up the AST. return computeFollowNode(fromNode, parent, cfa); } } /** * Computes the destination node of n when we want to fallthough into the * subtree of n. We don't always create a CFG edge into n itself because of * DOs and FORs. */ static Node computeFallThrough(Node n) { switch (n.getType()) { case Token.DO: return computeFallThrough(n.getFirstChild()); case Token.FOR: if (NodeUtil.isForIn(n)) { return n; } return computeFallThrough(n.getFirstChild()); case Token.LABEL: return computeFallThrough(n.getLastChild()); default: return n; } } /** * Connects the two nodes in the control flow graph. * * @param fromNode Source. * @param toNode Destination. */ private void createEdge(Node fromNode, ControlFlowGraph.Branch branch, Node toNode) { cfg.createNode(fromNode); cfg.createNode(toNode); cfg.connectIfNotFound(fromNode, branch, toNode); } /** * Connects cfgNode to the proper CATCH block if target subtree might throw * an exception. If there are FINALLY blocks reached before a CATCH, it will * make the corresponding entry in finallyMap. */ private void connectToPossibleExceptionHandler(Node cfgNode, Node target) { if (mayThrowException(target) && !exceptionHandler.isEmpty()) { Node lastJump = cfgNode; for (Node handler : exceptionHandler) { if (handler.isFunction()) { return; } Preconditions.checkState(handler.isTry()); Node catchBlock = NodeUtil.getCatchBlock(handler); if (!NodeUtil.hasCatchHandler(catchBlock)) { // No catch but a FINALLY. if (lastJump == cfgNode) { createEdge(cfgNode, Branch.ON_EX, handler.getLastChild()); } else { finallyMap.put(lastJump, handler.getLastChild()); } } else { // Has a catch. if (lastJump == cfgNode) { createEdge(cfgNode, Branch.ON_EX, catchBlock); return; } else { finallyMap.put(lastJump, catchBlock); } } lastJump = handler; } } } /** * Get the next sibling (including itself) of one of the given types. */ private static Node getNextSiblingOfType(Node first, int ... types) { for (Node c = first; c != null; c = c.getNext()) { for (int type : types) {

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> if (c.getType() == type) { return c; } } } return null; } /** * Checks if target is actually the break target of labeled continue. The * label can be null if it is an unlabeled break. */ public static boolean isBreakTarget(Node target, String label) { return isBreakStructure(target, label != null) && matchLabel(target.getParent(), label); } /** * Checks if target is actually the continue target of labeled continue. The * label can be null if it is an unlabeled continue. */ private static boolean isContinueTarget( Node target, Node parent, String label) { return isContinueStructure(target) && matchLabel(parent, label); } /** * Check if label is actually referencing the target control structure. If * label is null, it always returns true. */ private static boolean matchLabel(Node target, String label) { if (label == null) { return true; } while (target.isLabel()) { if (target.getFirstChild().getString().equals(label)) { return true; } target = target.getParent(); } return false; } /** * Determines if the subtree might throw an exception. */ public static boolean mayThrowException(Node n) { switch (n.getType()) { case Token.CALL: case Token.GETPROP: case Token.GETELEM: case Token.THROW: case Token.NEW: case Token.ASSIGN: case Token.INC: case Token.DEC: case Token.INSTANCEOF: return true; case Token.FUNCTION: return false; } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (!ControlFlowGraph.isEnteringNewCfgNode(c) && mayThrowException(c)) { return true; } } return false; } /** * Determines whether the given node can be terminated with a BREAK node. */ static boolean isBreakStructure(Node n, boolean labeled) { switch (n.getType()) { case Token.FOR: case Token.DO: case Token.WHILE: case Token.SWITCH: return true; case Token.BLOCK: case Token.IF: case Token.TRY: return labeled; default: return false; } } /** * Determines whether the given node can be advanced with a CONTINUE node. */ static boolean isContinueStructure(Node n) { switch (n.getType()) { case Token.FOR: case Token.DO: case Token.WHILE: return true; default: return false; } } /** * Get the TRY block with a CATCH that would be run if n throws an exception. * @return

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> The CATCH node or null if it there isn't a CATCH before the * the function terminates. */ static Node getExceptionHandler(Node n) { for (Node cur = n; cur.getType() != Token.SCRIPT && cur.getType() != Token.FUNCTION; cur = cur.getParent()) { Node catchNode = getCatchHandlerForBlock(cur); if (catchNode != null) { return catchNode; } } return null; } /** * Locate the catch BLOCK given the first block in a TRY. * @return The CATCH node or null there is no catch handler. */ static Node getCatchHandlerForBlock(Node block) { if (block.isBlock() && block.getParent().isTry() && block.getParent().getFirstChild() == block) { for (Node s = block.getNext(); s != null; s = s.getNext()) { if (NodeUtil.hasCatchHandler(s)) { return s.getFirstChild(); } } } return null; } /** * A {@link ControlFlowGraph} which provides a node comparator based on the * pre-order traversal of the AST. */ private static class AstControlFlowGraph extends ControlFlowGraph<Node> { private final Map<DiGraphNode<Node, Branch>, Integer> priorities; /** * Constructor. * @param entry The entry node. * @param priorities The map from nodes to position in the AST (to be * filled by the {@link ControlFlowAnalysis#shouldTraverse}). */ private AstControlFlowGraph(Node entry, Map<DiGraphNode<Node, Branch>, Integer> priorities, boolean edgeAnnotations) { super(entry, true /* node annotations */, edgeAnnotations); this.priorities = priorities; } @Override /** * Returns a node comparator based on the pre-order traversal of the AST. * @param isForward x 'before' y in the pre-order traversal implies * x 'less than' y (if true) and x 'greater than' y (if false). */ public Comparator<DiGraphNode<Node, Branch>> getOptionalNodeComparator( boolean isForward) { if (isForward) { return new Comparator<DiGraphNode<Node, Branch>>() { @Override public int compare( DiGraphNode<Node, Branch> n1, DiGraphNode<Node, Branch> n2) { return getPosition(n1) - getPosition(n2); } }; } else { return new Comparator<DiGraphNode<Node, Branch>>() { @Override public int compare( DiGraphNode<Node, Branch> n1, DiGraphNode<Node, Branch> n2) { return getPosition(n2) - getPosition(n1); } }; }

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> } /** * Gets the pre-order traversal position of the given node. * @return An arbitrary counter used for comparing positions. */ private int getPosition(DiGraphNode<Node, Branch> n) { Integer priority = priorities.get(n); Preconditions.checkNotNull(priority); return priority; } } }

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> RecordProperty> properties) { return new RecordType(this, properties); } /** * Create an object type. */ public ObjectType createObjectType(String name, Node n, ObjectType implicitPrototype) { return new PrototypeObjectType(this, name, implicitPrototype); } /** * Create an anonymous object type. */ public ObjectType createAnonymousObjectType() { PrototypeObjectType type = new PrototypeObjectType(this, null, null); type.setPrettyPrint(true); return type; } /** * Set the implicit prototype if it's possible to do so. * @return True if we were able to set the implicit prototype successfully, * false if it was not possible to do so for some reason. There are * a few different reasons why this could fail: for example, numbers * can't be implicit prototypes, and we don't want to change the implicit * prototype if other classes have already subclassed this one. */ public boolean resetImplicitPrototype( JSType type, ObjectType newImplicitProto) { if (type instanceof PrototypeObjectType) { PrototypeObjectType poType = (PrototypeObjectType) type; poType.clearCachedValues(); poType.setImplicitPrototype(newImplicitProto); return true; } return false; } /** * Create an anonymous object type for a native type. */ ObjectType createNativeAnonymousObjectType() { PrototypeObjectType type = new PrototypeObjectType(this, null, null, true); type.setPrettyPrint(true); return type; } /** * Creates a constructor function type. * @param name the function's name or {@code null} to indicate that the * function is anonymous. * @param source the node defining this function. Its type * ({@link Node#getType()}) must be {@link Token#FUNCTION}. * @param parameters the function's parameters or {@code null} * to indicate that the parameter types are unknown. * @param returnType the function's return type or {@code null} to indicate * that the return type is unknown. */ public FunctionType createConstructorType(String name, Node source, Node parameters, JSType returnType) { return new FunctionType(this, name, source, createArrowType(parameters, returnType), null, null, true, false); } /** * Creates an interface function type. * @param name the function's name * @param source the node defining this function. Its type * ({@link Node#getType()}) must be {@link Token#FUNCTION}. */ public FunctionType createInterfaceType(String name, Node source) { return FunctionType.forInterface(this, name, source); } /** * Creates a parameterized type. */ public ParameterizedType createParameterizedType( ObjectType objectType, JSType parameterType)

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> { return new ParameterizedType(this, objectType, parameterType); } /** * Creates a named type. */ @VisibleForTesting public JSType createNamedType(String reference, String sourceName, int lineno, int charno) { return new NamedType(this, reference, sourceName, lineno, charno); } /** * Identifies the name of a typedef or enum before we actually declare it. */ public void identifyNonNullableName(String name) { Preconditions.checkNotNull(name); nonNullableTypeNames.add(name); } /** * Creates a JSType from the nodes representing a type. * @param n The node with type info. * @param sourceName The source file name. * @param scope A scope for doing type name lookups. */ public JSType createFromTypeNodes(Node n, String sourceName, StaticScope<JSType> scope) { if (resolveMode == ResolveMode.LAZY_EXPRESSIONS) { // If the type expression doesn't contain any names, just // resolve it anyway. boolean hasNames = hasTypeName(n); if (hasNames) { return new UnresolvedTypeExpression(this, n, sourceName); } } return createFromTypeNodesInternal(n, sourceName, scope); } private boolean hasTypeName(Node n) { if (n.getType() == Token.STRING) { return true; } for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { if (hasTypeName(child)) { return true; } } return false; } /** @see #createFromTypeNodes(Node, String, StaticScope) */ private JSType createFromTypeNodesInternal(Node n, String sourceName, StaticScope<JSType> scope) { switch (n.getType()) { case Token.LC: // Record type. return createRecordTypeFromNodes( n.getFirstChild(), sourceName, scope); case Token.BANG: // Not nullable return createFromTypeNodesInternal( n.getFirstChild(), sourceName, scope) .restrictByNotNullOrUndefined(); case Token.QMARK: // Nullable or unknown Node firstChild = n.getFirstChild(); if (firstChild == null) { return getNativeType(UNKNOWN_TYPE); } return createDefaultObjectUnion( createFromTypeNodesInternal( firstChild, sourceName, scope)); case Token.EQUALS: // Optional return createOptionalType( createFromTypeNodesInternal( n.getFirstChild(), sourceName, scope)); case Token.ELLIPSIS: // Var args return createOptionalType( createFromTypeNodesInternal( n.getFirstChild(), sourceName, scope)); case Token.STAR: // The AllType return getNativeType(ALL_TYPE);

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> case Token.LB: // Array type // TODO(nicksantos): Enforce membership restrictions on the Array. return getNativeType(ARRAY_TYPE); case Token.PIPE: // Union type UnionTypeBuilder builder = new UnionTypeBuilder(this); for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { builder.addAlternate( createFromTypeNodesInternal(child, sourceName, scope)); } return builder.build(); case Token.EMPTY: // When the return value of a function is not specified return getNativeType(UNKNOWN_TYPE); case Token.VOID: // Only allowed in the return value of a function. return getNativeType(VOID_TYPE); case Token.STRING: JSType namedType = getType(scope, n.getString(), sourceName, n.getLineno(), n.getCharno()); if (resolveMode != ResolveMode.LAZY_NAMES) { namedType = namedType.resolveInternal(reporter, scope); } if ((namedType instanceof ObjectType) && !(nonNullableTypeNames.contains(n.getString()))) { Node typeList = n.getFirstChild(); if (typeList != null && ("Array".equals(n.getString()) || "Object".equals(n.getString()))) { JSType parameterType = createFromTypeNodesInternal( typeList.getLastChild(), sourceName, scope); namedType = new ParameterizedType( this, (ObjectType) namedType, parameterType); if (typeList.hasMoreThanOneChild()) { JSType indexType = createFromTypeNodesInternal( typeList.getFirstChild(), sourceName, scope); namedType = new IndexedType( this, (ObjectType) namedType, indexType); } } return createDefaultObjectUnion(namedType); } else { return namedType; } case Token.FUNCTION: ObjectType thisType = null; boolean isConstructor = false; Node current = n.getFirstChild(); if (current.getType() == Token.THIS || current.getType() == Token.NEW) { Node contextNode = current.getFirstChild(); thisType = ObjectType.cast( createFromTypeNodesInternal( contextNode, sourceName, scope) .restrictByNotNullOrUndefined()); if (thisType == null) { reporter.warning( ScriptRuntime.getMessage0( current.getType() == Token.THIS ? "msg.jsdoc.function.thisnotobject" : "msg.jsdoc.function.newnotobject"), sourceName, contextNode.getLineno(), contextNode.getCharno()); } isConstructor = current.getType() == Token.NEW; current = current.getNext(); } FunctionParamBuilder paramBuilder = new FunctionParamBuilder(this); if (current.getType() == Token.PARAM

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>_LIST) { Node args = current.getFirstChild(); for (Node arg = current.getFirstChild(); arg != null; arg = arg.getNext()) { if (arg.getType() == Token.ELLIPSIS) { if (arg.getChildCount() == 0) { paramBuilder.addVarArgs(getNativeType(UNKNOWN_TYPE)); } else { paramBuilder.addVarArgs( createFromTypeNodesInternal( arg.getFirstChild(), sourceName, scope)); } } else { JSType type = createFromTypeNodesInternal( arg, sourceName, scope); if (arg.getType() == Token.EQUALS) { boolean addSuccess = paramBuilder.addOptionalParams(type); if (!addSuccess) { reporter.warning( ScriptRuntime.getMessage0("msg.jsdoc.function.varargs"), sourceName, arg.getLineno(), arg.getCharno()); } } else { paramBuilder.addRequiredParams(type); } } } current = current.getNext(); } JSType returnType = createFromTypeNodesInternal(current, sourceName, scope); return new FunctionBuilder(this) .withParams(paramBuilder) .withReturnType(returnType) .withTypeOfThis(thisType) .setIsConstructor(isConstructor) .build(); } throw new IllegalStateException( "Unexpected node in type expression: " + n.toString()); } /** * Creates a RecordType from the nodes representing said record type. * @param n The node with type info. * @param sourceName The source file name. * @param scope A scope for doing type name lookups. */ private JSType createRecordTypeFromNodes(Node n, String sourceName, StaticScope<JSType> scope) { RecordTypeBuilder builder = new RecordTypeBuilder(this); // For each of the fields in the record type. for (Node fieldTypeNode = n.getFirstChild(); fieldTypeNode != null; fieldTypeNode = fieldTypeNode.getNext()) { // Get the property's name. Node fieldNameNode = fieldTypeNode; boolean hasType = false; if (fieldTypeNode.getType() == Token.COLON) { fieldNameNode = fieldTypeNode.getFirstChild(); hasType = true; } String fieldName = fieldNameNode.getString(); // TODO(user): Move this into the lexer/parser. // Remove the string literal characters around a field name, // if any. if (fieldName.startsWith("'") || fieldName.startsWith("\"")) { fieldName = fieldName.substring(1, fieldName.length() - 1); } // Get the property's type. JSType fieldType = null; if (hasType) { // We have a declared type. fieldType = createFromTypeNodesInternal( fieldTypeNode.getLastChild(), sourceName, scope);

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> } else { // Otherwise, the type is UNKNOWN. fieldType = getNativeType(JSTypeNative.UNKNOWN_TYPE); } // Add the property to the record. if (builder.addProperty(fieldName, fieldType, fieldNameNode) == null) { // Duplicate field name, warning and skip reporter.warning( "Duplicate record field " + fieldName, sourceName, n.getLineno(), fieldNameNode.getCharno()); } } return builder.build(); } /** * Sets the template type name. */ public void setTemplateTypeName(String name) { templateTypeName = name; templateType = new TemplateType(this, name); } /** * Clears the template type name. */ public void clearTemplateTypeName() { templateTypeName = null; templateType = null; } }

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>(N srcValue, Branch edgeValue) { super.connect(srcValue, edgeValue, null); } /** * Gets a comparator for the nodes. The default implementation returns * {@code null}. See {@link ControlFlowGraph#getOptionalNodeComparator}. * @param isForward Whether the comparator sorts the nodes in the direction of * the flow. * @return a comparator or null (in particular, if not overriden) */ public Comparator<DiGraphNode<N, Branch>> getOptionalNodeComparator( boolean isForward) { return null; } /** * The edge object for the control flow graph. */ public static enum Branch { /** Edge is taken if the condition is true. */ ON_TRUE, /** Edge is taken if the condition is false. */ ON_FALSE, /** Unconditional branch. */ UNCOND, /** Exception related. */ ON_EX, /** Possible folded-away template */ SYN_BLOCK; public boolean isConditional() { return this == ON_TRUE || this == ON_FALSE; } } /** * Abstract callback to visit a control flow graph node without going into * subtrees of the node that is also represented by another control flow graph * node. * * <p>For example, traversing an IF node as root will visit the two subtree * pointed by the {@link ControlFlowGraph.Branch#ON_TRUE} and * {@link ControlFlowGraph.Branch#ON_FALSE} edge. */ public abstract static class AbstractCfgNodeTraversalCallback implements Callback { @Override public final boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { if (parent == null) { return true; } return !isEnteringNewCfgNode(n); } } /** * @return True if n should be represented by a new CFG node in the control * flow graph. */ public static boolean isEnteringNewCfgNode(Node n) { Node parent = n.getParent(); switch (parent.getType()) { case Token.BLOCK: case Token.SCRIPT: case Token.TRY: return true; case Token.FUNCTION: // A function node represents the start of a function where the name // is bleed into the local scope and parameters has been assigned // to the formal argument names. The node includes the name of the // function and the LP list since we assume the whole set up process // is atomic without change in control flow. The next change of // control is going into the function's body represent by the second // child. return n != parent.getFirstChild().getNext(); case Token.WHILE: case Token.DO: case Token.IF: // Theses control structure is represented by its node that holds the // condition. Each of them is a branch node based on its

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> condition. return NodeUtil.getConditionExpression(parent) != n; case Token.FOR: // The FOR(;;) node differs from other control structure in that // it has a initialization and a increment statement. Those // two statements have its corresponding CFG nodes to represent them. // The FOR node represents the condition check for each iteration. // That way the following: // for(var x = 0; x < 10; x++) { } has a graph that is isomorphic to // var x = 0; while(x<10) { x++; } if (NodeUtil.isForIn(parent)) { return n == parent.getLastChild(); } else { return NodeUtil.getConditionExpression(parent) != n; } case Token.SWITCH: case Token.CASE: case Token.CATCH: case Token.WITH: return n != parent.getFirstChild(); default: return false; } } }

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS>DeclaredProperty("length", numberType, null); } private static void addMethod( JSTypeRegistry registry, ObjectType receivingType, String methodName, JSType returnType) { receivingType.defineDeclaredProperty(methodName, new FunctionBuilder(registry).withReturnType(returnType).build(), null); } protected JSType createUnionType(JSType... variants) { return registry.createUnionType(variants); } protected RecordTypeBuilder createRecordTypeBuilder() { return new RecordTypeBuilder(registry); } protected JSType createNullableType(JSType type) { return registry.createNullableType(type); } protected JSType createOptionalType(JSType type) { return registry.createOptionalType(type); } /** * Asserts that a Node representing a type expression resolves to the * correct {@code JSType}. */ protected void assertTypeEquals(JSType expected, Node actual) { assertTypeEquals(expected, new JSTypeExpression(actual, "")); } /** * Asserts that a a type expression resolves to the correct {@code JSType}. */ protected void assertTypeEquals(JSType expected, JSTypeExpression actual) { assertEquals(expected, resolve(actual)); } /** * Resolves a type expression, expecting the given warnings. */ protected JSType resolve(JSTypeExpression n, String... warnings) { errorReporter.setWarnings(warnings); return n.evaluate(null, registry); } /** * A definition of all extern types. This should be kept in sync with * javascript/externs/es3.js. This is used to check that the builtin types * declared in {@link JSTypeRegistry} have the same type as that in the * externs. It can also be used for any tests that want to use builtin types * in their externs. */ public static final String ALL_NATIVE_EXTERN_TYPES = "/**\n" + " * @constructor\n" + " * @param {*} opt_value\n" + " */\n" + "function Object(opt_value) {}\n" + "\n" + "/**\n" + " * @constructor\n" + " * @extends {Object}\n" + " * @param {*} var_args\n" + " */\n" + "\n" + "function Function(var_args) {}\n" + "/**\n" + " * @constructor\n" + " * @extends {Object}\n" + " * @param {*} var_args\n" + " * @return {!Array}\n" + " */\n" + "function Array(var_args) {}\n" + "\n" + "/**\n" + " * @constructor\n" + " * @param {*}

Closure, 48

<FILEB>
<CHANGES>
if (inferred && rhsValue!= null && rhsValue.isFunction()) {
<CHANGEE>
<CHANGES>
if (info!= null) {
inferred = false;
} else if (!scope.isDeclared(qName, false) &&
n.isUnscopedQualifiedName()) {
inferred = false;
}
<CHANGEE>
<FILEE>
<FILEB> // // In a dynamic language with first-class functions, it's very difficult // to know which one the user intended without looking at lots of // contextual information (the second example demonstrates a small case // of this, but there are some really pathological cases as well). // // The current algorithm checks if either the declaration has // jsdoc type information, or @const with a known type, // or a function literal with a name we haven't seen before. boolean inferred = true; if (info != null) { // Determining declaration for #1 + #3 + #4 inferred = !(info.hasType() || info.hasEnumParameterType() || (info.isConstant() && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } <CHANGES> if (inferred) { <CHANGEE> // Determining declaration for #2 <CHANGES> inferred = !(rhsValue != null && rhsValue.isFunction() && (info != null || !scope.isDeclared(qName, false))); <CHANGEE> } if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. <FILEE> <SCANS> } this.call = arrowType; this.templateTypeName = templateTypeName; } /** Creates an instance for a function that is an interface. */ private FunctionType(JSTypeRegistry registry, String name, Node source) { super(registry, name, registry.getNativeObjectType(JSTypeNative.FUNCTION_INSTANCE_TYPE)); setPrettyPrint(true); Preconditions.checkArgument(source == null || Token.FUNCTION == source.getType()); Preconditions.checkArgument(name != null); this.source = source; this.call = new ArrowType(registry, new Node(Token.PARAM_LIST), null); this.kind = Kind.INTERFACE; this.typeOfThis = new InstanceObjectType(registry, this); } /** Creates an instance for a function that is an interface. */ static FunctionType forInterface( JSTypeRegistry registry, String name, Node source) { return new FunctionType(registry, name, source); } @Override public boolean isInstanceType() { // The universal constructor is its own instance, bizarrely. return isEquivalentTo(registry.getNativeType(U2U_CONSTRUCTOR_TYPE)); } @Override public boolean isConstructor() { return kind == Kind.CONSTRUCTOR; } @Override public boolean isInterface() { return kind == Kind.INTERFACE; } @Override public boolean isOrdinaryFunction() { return kind == Kind.ORDINARY; } @Override public FunctionType toMaybeFunctionType() { return this; } @Override public boolean canBeCalled() { return true; } public boolean hasImplementedInterfaces() { if (!implementedInterfaces.isEmpty()){ return true; } FunctionType superCtor = isConstructor() ? getSuperClassConstructor() : null; if (superCtor != null) { return superCtor.hasImplementedInterfaces(); } return false; } public Iterable<Node> getParameters() { Node n = getParametersNode(); if (n != null) { return n.children(); } else { return Collections.emptySet(); } } /** Gets an LP node that contains all params. May be null. */ public Node getParametersNode() { return call.parameters; } /** Gets the minimum number of arguments that this function requires. */ public int getMinArguments() { // NOTE(nicksantos): There are some native functions that have optional // parameters before required parameters. This algorithm finds the position // of the last required parameter. int i = 0; int min = 0; for (Node n : getParameters()) { i++; if (!n.isOptionalArg() && !n.isVarArgs()) { min = i; } } return min; } /** * Gets the maximum number of arguments that this function